package org.webpieces.httpparser.impl.subparsers; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.function.Function; import org.webpieces.util.logging.Logger; import org.webpieces.util.logging.LoggerFactory; import org.webpieces.httpparser.api.common.Header; import org.webpieces.httpparser.api.common.KnownHeaderName; import org.webpieces.httpparser.api.common.ResponseCookie; import org.webpieces.httpparser.api.dto.HttpRequest; import org.webpieces.httpparser.api.subparsers.AcceptType; import org.webpieces.httpparser.api.subparsers.HeaderItem; import org.webpieces.httpparser.api.subparsers.HeaderPriorityParser; public class HeaderPriorityParserImpl implements HeaderPriorityParser { private static final Logger log = LoggerFactory.getLogger(HeaderPriorityParserImpl.class); @Override public List<String> parseAcceptEncoding(HttpRequest req) { Header langHeader = req.getHeaderLookupStruct().getHeader(KnownHeaderName.ACCEPT_ENCODING); if(langHeader == null) return new ArrayList<>(); List<String> headerItems = parsePriorityItems(langHeader.getValue(), s -> s); return headerItems; } @Override public List<Locale> parseAcceptLangFromRequest(HttpRequest req) { Header langHeader = req.getHeaderLookupStruct().getHeader(KnownHeaderName.ACCEPT_LANGUAGE); if(langHeader == null) return new ArrayList<>(); List<Locale> headerItems = parsePriorityItems(langHeader.getValue(), s -> parseItem(s)); return headerItems; } private Locale parseItem(String subItem) { //Parse the locale Locale locale = null; String[] l = subItem.split("_"); switch(l.length){ case 2: locale = new Locale(l[0], l[1]); break; case 3: locale = new Locale(l[0], l[1], l[2]); break; default: locale = new Locale(l[0]); break; } return locale; } @Override public <T> List<T> parsePriorityItems(String value, Function<String, T> parseFunction) { List<HeaderItem<T>> headerItems = new ArrayList<>(); for (String str : value.split(",")){ String[] arr = str.trim().replace("-", "_").split(";"); T item = parseFunction.apply(arr[0]); if(item == null) continue; //Parse the q-value Double q = 1.0D; for (String s : arr){ s = s.trim(); if (s.startsWith("q=")){ q = Double.parseDouble(s.substring(2).trim()); break; } } //Print the Locale and associated q-value HeaderItem<T> headerItem = new HeaderItem<>(q, item); headerItems.add(headerItem); } Collections.sort(headerItems); List<T> orderedItems = new ArrayList<>(); for(HeaderItem<T> item : headerItems) { orderedItems.add(item.getItem()); } return orderedItems; } @Override public Map<String, String> parseCookiesFromRequest(HttpRequest req) { Header cookieHeader = req.getHeaderLookupStruct().getHeader(KnownHeaderName.COOKIE); if(cookieHeader == null) return new HashMap<>(); String value = cookieHeader.getValue(); String[] split = value.trim().split(";"); Map<String, String> map = new HashMap<>(); for(String keyValPair : split) { //there are many = signs but the first one is the cookie name...the other are embedded key=value pairs int index = keyValPair.indexOf("="); String name = keyValPair.substring(0, index).trim(); String val = keyValPair.substring(index+1).trim(); map.put(name, val); } return map; } /** * From https://www.owasp.org/index.php/HttpOnly * * Set-Cookie: <name>=<value>[; <Max-Age>=<age>] * [; expires=<date>][; domain=<domain_name>] * [; path=<some_path>][; secure][; HttpOnly] * * and http://mrcoles.com/blog/cookies-max-age-vs-expires/ * * @param cookie * @return */ @Override public Header createHeader(ResponseCookie cookie) { String name = cookie.getName(); String value = cookie.getValue(); Integer maxAgeSeconds = cookie.getMaxAgeSeconds(); String domain = cookie.getDomain(); String path = cookie.getPath(); boolean isHttpOnly = cookie.isHttpOnly(); boolean isSecure = cookie.isSecure(); String headerVal = ""; if(name != null) headerVal = name +"="; else if(value == null) throw new IllegalArgumentException("value in cookie cannot be null"); headerVal += value; if(maxAgeSeconds != null) headerVal += "; Max-Age="+maxAgeSeconds; if(domain != null) headerVal += "; domain="+domain; if(path != null) headerVal += "; path="+path; if(isSecure) headerVal += "; secure"; if(isHttpOnly) headerVal += "; HttpOnly"; return new Header(KnownHeaderName.SET_COOKIE, headerVal); } @Override public List<AcceptType> parseAcceptFromRequest(HttpRequest req) { List<AcceptType> list = new ArrayList<>(); Header header = req.getHeaderLookupStruct().getHeader(KnownHeaderName.ACCEPT); if(header == null) return list; String value = header.getValue(); return parsePriorityItems(value, s -> parseAcceptSubitem(s)); } private AcceptType parseAcceptSubitem(String subItem) { String[] pieces = subItem.trim().split("/"); if(pieces.length != 2) { log.warn("subItem not valid since missing / item="+subItem+". we are skipping it"); return null; } if("*".equals(pieces[0])) { if("*".equals(pieces[1])) { return new AcceptType(); } else { log.warn("subItem not valid since missing */"+pieces[1]+" is not allowed in spec. item="+subItem+". we are skipping it"); return null; } } else if("*".equals(pieces[1])) { return new AcceptType(pieces[1]); } return new AcceptType(pieces[0], pieces[1]); } }