All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.vertx.ext.web.impl.HeaderParser Maven / Gradle / Ivy

There is a newer version: 5.0.0.CR1
Show newest version
package io.vertx.ext.web.impl;

import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.ext.web.ParsedHeaderValue;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * Build with the intent of following
 * rfc7231,section-5.3.1's specification.
*/ public class HeaderParser { private static final Logger LOG = LoggerFactory.getLogger(HeaderParser.class); private static final Comparator HEADER_SORTER = (ParsedHeaderValue left, ParsedHeaderValue right) -> right.weightedOrder() - left.weightedOrder(); /** * Transforms each header value into the given ParsableHeaderValue * * @param unparsedHeaderValue The header to split * @param objectCreator The type to instantiate for each header * @return The list of (unparsed) parsable header value */ public static List convertToParsedHeaderValues(String unparsedHeaderValue, Function objectCreator) { return split(unparsedHeaderValue, ',', objectCreator); } /** * In-place sorting of the headers list * * @param headers * @return The same object as inserted */ public static List sort(List headers) { headers.sort(HEADER_SORTER); return headers; } /** * Parses a header value * * @param headerContent * @param valueCallback * @param weightCallback * @param parameterCallback */ public static void parseHeaderValue(String headerContent, Consumer valueCallback, Consumer weightCallback, BiConsumer parameterCallback) { int paramIndex = headerContent.indexOf(';'); if (paramIndex < 0) { valueCallback.accept(headerContent); } else { // the whole value valueCallback.accept(headerContent.substring(0, paramIndex)); if (paramIndex < headerContent.length()) { split(headerContent.substring(paramIndex + 1), ';', part -> { int idx = part.indexOf('='); if (idx != -1) { final String key = part.substring(0, idx); final String val = part.substring(idx + 1); if ("q".equalsIgnoreCase(key)) { try { weightCallback.accept(Float.parseFloat(val)); } catch (NumberFormatException e) { if (LOG.isTraceEnabled()) LOG.trace("Found a \"q\" parameter with value \""+val+"\" that was unparsable"); } } else { parameterCallback.accept(key, unquote(val)); } } else { // no value associated with this key parameterCallback.accept(part, null); } return null; }); } } } public static void parseMIME( String headerContent, Consumer componentCallback, Consumer subcomponentCallback ) { int slashIndex = headerContent.indexOf('/'); int paramIndex = headerContent.indexOf(';', slashIndex + 1); if (slashIndex < 0) { componentCallback.accept("*"); } else { componentCallback.accept(headerContent.substring(0, slashIndex).toLowerCase()); } if (paramIndex < 0) { subcomponentCallback.accept(headerContent.substring(slashIndex + 1)); } else { subcomponentCallback.accept(headerContent.substring(slashIndex + 1, paramIndex).toLowerCase()); } } public static List parseLanguageValue(String value) { if (value == null || value.length() == 0) { return Collections.emptyList(); } final List parts = new LinkedList<>(); // state machine int start = 0; for (int i = 0; i < value.length(); i++) { char ch = value.charAt(i); // trim initial white space if (start == i && ch == ' ') { start++; continue; } // splitting logic uses 2 chars, since java Locales use underscore if (ch == '-' || ch == '_') { int end = i; // trim end white space for (int j = i - 1; j >= start; j--) { if (value.charAt(j) == ' ') { end--; continue; } break; } // ignore empty if (end - start > 0) { parts.add(value.substring(start, end)); if (parts.size() == 3) { // force stop, we have country, language and variant return parts; } } start = i + 1; } } // rest if (start < value.length()) { int end = value.length(); // trim end white space for (int j = value.length() - 1; j >= start; j--) { if (value.charAt(j) == ' ') { end--; continue; } break; } // ignore empty if (end - start > 0) { parts.add(value.substring(start, end)); } } return parts; } private static List split(String header, char split, Function factory) { if (header == null || header.length() == 0) { return Collections.emptyList(); } final List parts = new LinkedList<>(); // state machine boolean quote = false; int start = 0; char last = 0; for (int i = 0; i < header.length(); i++) { char ch = header.charAt(i); // trim initial white space if (start == i && ch == ' ') { start++; continue; } // identify if we're handling quoted strings if (ch == '\"' && last != '\\') { quote = !quote; } last = ch; // splitting logic only applies outside quoted strings if (!quote && ch == split) { int end = i; // trim end white space for (int j = i - 1; j >= start; j--) { if (header.charAt(j) == ' ') { end--; continue; } break; } // ignore empty if (end - start > 0) { parts.add(factory.apply(header.substring(start, end))); } start = i + 1; } } // rest if (start < header.length()) { int end = header.length(); // trim end white space for (int j = header.length() - 1; j >= start; j--) { if (header.charAt(j) == ' ') { end--; continue; } break; } // ignore empty if (end - start > 0) { parts.add(factory.apply(header.substring(start, end))); } } return parts; } private static String unquote(String value) { if (value == null || value.length() == 0) { return value; } StringBuilder sb = null; int start = 0; int end = value.length(); // adjust start if there is a quote if (value.charAt(start) == '\"') { start++; } // adjust end if there is a quote if (value.charAt(end - 1) == '\"') { end--; } // look for extra quotes in the value itself for (int i = start ; i < end; i++) { if (value.charAt(i) == '\\') { if (sb == null) { sb = new StringBuilder(value.substring(start, i)); } continue; } if (sb != null) { sb.append(value.charAt(i)); } } if (sb != null) { return sb.toString(); } else { // the value is quoted if (end - start != value.length()) { return value.substring(start, end); } return value; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy