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

io.undertow.util.QValueParser Maven / Gradle / Ivy

There is a newer version: 2.3.18.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package io.undertow.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Utility class for parsing headers that accept q values
 *
 * @author Stuart Douglas
 */
public class QValueParser {

    private QValueParser() {

    }

    /**
     * Parses a set of headers that take q values to determine the most preferred one.
     *
     * It returns the result in the form of a sorted list of list, with every element in
     * the list having the same q value. This means the highest priority items are at the
     * front of the list. The container should use its own internal preferred ordering
     * to determinately pick the correct item to use
     *
     * @param headers The headers
     * @return The q value results
     */
    public static List> parse(List headers) {
        final List found = new ArrayList<>();
        QValueResult current = null;
        //noinspection ForLoopReplaceableByForEach - using induction for loop for iteration to avoid allocation
        for (int j = 0; j < headers.size(); j++) {
            final String header = headers.get(j);
            final int l = header.length();
            //we do not use a string builder
            //we just keep track of where the current string starts and call substring()
            int stringStart = 0;
            for (int i = 0; i < l; ++i) {
                char c = header.charAt(i);
                switch (c) {
                    case ',': {
                        if (current != null &&
                                (i - stringStart > 2 && header.charAt(stringStart) == 'q' &&
                                        header.charAt(stringStart + 1) == '=')) {
                            //if this is a valid qvalue
                            current.qvalue = header.substring(stringStart + 2, i);
                            current = null;
                        } else if (stringStart != i) {
                            current = handleNewEncoding(found, header, stringStart, i);
                        }
                        stringStart = i + 1;
                        break;
                    }
                    case ';': {
                        if (stringStart != i) {
                            current = handleNewEncoding(found, header, stringStart, i);
                            stringStart = i + 1;
                        }
                        break;
                    }
                    case ' ': {
                        if (stringStart != i) {
                            if (current != null &&
                                    (i - stringStart > 2 && header.charAt(stringStart) == 'q' &&
                                            header.charAt(stringStart + 1) == '=')) {
                                //if this is a valid qvalue
                                current.qvalue = header.substring(stringStart + 2, i);
                            } else {
                                current = handleNewEncoding(found, header, stringStart, i);
                            }
                        }
                        stringStart = i + 1;
                    }
                }
            }

            if (stringStart != l) {
                if (current != null &&
                        (l - stringStart > 2 && header.charAt(stringStart) == 'q' &&
                                header.charAt(stringStart + 1) == '=')) {
                    //if this is a valid qvalue
                    current.qvalue = header.substring(stringStart + 2, l);
                } else {
                    current = handleNewEncoding(found, header, stringStart, l);
                }
            }
        }
        Collections.sort(found, Collections.reverseOrder());
        String currentQValue = null;
        List> values = new ArrayList<>();
        List currentSet = null;

        //noinspection ForLoopReplaceableByForEach - using induction for loop for iteration to avoid allocation
        for (int i = 0; i < found.size(); i++) {
            final QValueResult val = found.get(i);
            if (!val.qvalue.equals(currentQValue)) {
                currentQValue = val.qvalue;
                currentSet = new ArrayList<>();
                values.add(currentSet);
            }
            currentSet.add(val);
        }
        return values;
    }

    private static QValueResult handleNewEncoding(final List found, final String header, final int stringStart, final int i) {
        final QValueResult current = new QValueResult();
        current.value = header.substring(stringStart, i);
        found.add(current);
        return current;
    }

    public static class QValueResult implements Comparable {


        /**
         * The string value of the result
         */
        private String value;

        /**
         * we keep the qvalue as a string to avoid parsing the double.
         * 

* This should give both performance and also possible security improvements */ private String qvalue = "1"; public String getValue() { return value; } public String getQvalue() { return qvalue; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof QValueResult)) return false; QValueResult that = (QValueResult) o; if (getValue() != null ? !getValue().equals(that.getValue()) : that.getValue() != null) return false; return getQvalue() != null ? getQvalue().equals(that.getQvalue()) : that.getQvalue() == null; } @Override public int hashCode() { int result = getValue() != null ? getValue().hashCode() : 0; result = 31 * result + (getQvalue() != null ? getQvalue().hashCode() : 0); return result; } @Override public int compareTo(final QValueResult other) { //we compare the strings as if they were decimal values. //we know they can only be final String t = qvalue; final String o = other.qvalue; if (t == null && o == null) { //neither of them has a q value //we compare them via the server specified default precedence //note that encoding is never null here, a * without a q value is meaningless //and will be discarded before this return 0; } if (o == null) { return 1; } else if (t == null) { return -1; } final int tl = t.length(); final int ol = o.length(); //we only compare the first 5 characters as per spec for (int i = 0; i < 5; ++i) { if (tl == i || ol == i) { return ol - tl; //longer one is higher } if (i == 1) continue; // this is just the decimal point final int tc = t.charAt(i); final int oc = o.charAt(i); int res = tc - oc; if (res != 0) { return res; } } return 0; } public boolean isQValueZero() { //we ignore * without a qvalue if (qvalue != null) { int length = Math.min(5, qvalue.length()); //we need to find out if this is prohibiting identity //encoding (q=0). Otherwise we just treat it as the identity encoding boolean zero = true; for (int j = 0; j < length; ++j) { if (j == 1) continue;//decimal point if (qvalue.charAt(j) != '0') { zero = false; break; } } return zero; } return false; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy