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

org.apache.tomcat.util.http.parser.StructuredField Maven / Gradle / Ivy

There is a newer version: 11.0.0-M26
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.tomcat.util.http.parser;

import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.tomcat.util.res.StringManager;

/**
 * Parsing of structured fields as per RFC 8941.
 * 

* The parsing implementation is complete but not all elements are currently exposed via getters. Additional getters * will be added as required as the use of structured fields expands. *

* The serialization of structured fields has not been implemented. */ public class StructuredField { private static final StringManager sm = StringManager.getManager(StructuredField.class); private static final int ARRAY_SIZE = 128; private static final boolean[] IS_KEY_FIRST = new boolean[ARRAY_SIZE]; private static final boolean[] IS_KEY = new boolean[ARRAY_SIZE]; private static final boolean[] IS_OWS = new boolean[ARRAY_SIZE]; private static final boolean[] IS_BASE64 = new boolean[ARRAY_SIZE]; private static final boolean[] IS_TOKEN = new boolean[ARRAY_SIZE]; static { for (int i = 0; i < ARRAY_SIZE; i++) { if (i == '*' || i >= 'a' && i <= 'z') { IS_KEY_FIRST[i] = true; IS_KEY[i] = true; } else if (i >= '0' && i <= '9' || i == '_' || i == '-' || i == '.') { IS_KEY[i] = true; } } for (int i = 0; i < ARRAY_SIZE; i++) { if (i == 9 || i == ' ') { IS_OWS[i] = true; } } for (int i = 0; i < ARRAY_SIZE; i++) { if (i == '+' || i == '/' || i >= '0' && i <= '9' || i == '=' || i >= 'A' && i <= 'Z' || i >= 'a' && i <= 'z') { IS_BASE64[i] = true; } } for (int i = 0; i < ARRAY_SIZE; i++) { if (HttpParser.isToken(i) || i == ':' || i == '/') { IS_TOKEN[i] = true; } } } static SfList parseSfList(Reader input) throws IOException { skipSP(input); SfList result = new SfList(); if (peek(input) != -1) { while (true) { SfListMember listMember = parseSfListMember(input); result.addListMember(listMember); skipOWS(input); if (peek(input) == -1) { break; } requireChar(input, ','); skipOWS(input); requireNotChar(input, -1); } } skipSP(input); requireChar(input, -1); return result; } // Item or inner list static SfListMember parseSfListMember(Reader input) throws IOException { SfListMember listMember; if (peek(input) == '(') { listMember = parseSfInnerList(input); } else { listMember = parseSfBareItem(input); } parseSfParameters(input, listMember); return listMember; } static SfInnerList parseSfInnerList(Reader input) throws IOException { requireChar(input, '('); SfInnerList innerList = new SfInnerList(); while (true) { skipSP(input); if (peek(input) == ')') { break; } SfItem item = parseSfBareItem(input); parseSfParameters(input, item); innerList.addListItem(item); input.mark(1); requireChar(input, ' ', ')'); input.reset(); } requireChar(input, ')'); return innerList; } static SfDictionary parseSfDictionary(Reader input) throws IOException { skipSP(input); SfDictionary result = new SfDictionary(); if (peek(input) != -1) { while (true) { String key = parseSfKey(input); SfListMember listMember; input.mark(1); int c = input.read(); if (c == '=') { listMember = parseSfListMember(input); } else { listMember = new SfBoolean(true); input.reset(); } parseSfParameters(input, listMember); result.addDictionaryMember(key, listMember); skipOWS(input); if (peek(input) == -1) { break; } requireChar(input, ','); skipOWS(input); requireNotChar(input, -1); } } skipSP(input); requireChar(input, -1); return result; } static SfItem parseSfItem(Reader input) throws IOException { skipSP(input); SfItem item = parseSfBareItem(input); parseSfParameters(input, item); skipSP(input); requireChar(input, -1); return item; } static SfItem parseSfBareItem(Reader input) throws IOException { int c = input.read(); SfItem item; if (c == '-' || HttpParser.isNumeric(c)) { item = parseSfNumeric(input, c); } else if (c == '\"') { item = parseSfString(input); } else if (c == '*' || HttpParser.isAlpha(c)) { item = parseSfToken(input, c); } else if (c == ':') { item = parseSfByteSequence(input); } else if (c == '?') { item = parseSfBoolean(input); } else { throw new IllegalArgumentException( sm.getString("sf.bareitem.invalidCharacter", String.format("\\u%40X", Integer.valueOf(c)))); } return item; } static void parseSfParameters(Reader input, SfListMember listMember) throws IOException { while (true) { if (peek(input) != ';') { break; } requireChar(input, ';'); skipSP(input); String key = parseSfKey(input); SfItem item; input.mark(1); int c = input.read(); if (c == '=') { item = parseSfBareItem(input); } else { item = new SfBoolean(true); input.reset(); } listMember.addParameter(key, item); } } static String parseSfKey(Reader input) throws IOException { StringBuilder result = new StringBuilder(); input.mark(1); int c = input.read(); if (!isKeyFirst(c)) { throw new IllegalArgumentException( sm.getString("sf.key.invalidFirstCharacter", String.format("\\u%40X", Integer.valueOf(c)))); } while (c != -1 && isKey(c)) { result.append((char) c); input.mark(1); c = input.read(); } input.reset(); return result.toString(); } static SfItem parseSfNumeric(Reader input, int first) throws IOException { int sign = 1; boolean integer = true; int decimalPos = 0; StringBuilder result = new StringBuilder(); int c; if (first == '-') { sign = -1; c = input.read(); } else { c = first; } if (!HttpParser.isNumeric(c)) { throw new IllegalArgumentException( sm.getString("sf.numeric.invalidCharacter", String.format("\\u%40X", Integer.valueOf(c)))); } result.append((char) c); input.mark(1); c = input.read(); while (c != -1) { if (HttpParser.isNumeric(c)) { result.append((char) c); } else if (integer && c == '.') { if (result.length() > 12) { throw new IllegalArgumentException(sm.getString("sf.numeric.integralPartTooLong")); } integer = false; result.append((char) c); decimalPos = result.length(); } else { input.reset(); break; } if (integer && result.length() > 15) { throw new IllegalArgumentException(sm.getString("sf.numeric.integerTooLong")); } if (!integer && result.length() > 16) { throw new IllegalArgumentException(sm.getString("sf.numeric.decimalTooLong")); } input.mark(1); c = input.read(); } if (integer) { return new SfInteger(Long.parseLong(result.toString()) * sign); } if (result.charAt(result.length() - 1) == '.') { throw new IllegalArgumentException(sm.getString("sf.numeric.decimalInvalidFinal")); } if (result.length() - decimalPos > 3) { throw new IllegalArgumentException(sm.getString("sf.numeric.decimalPartTooLong")); } return new SfDecimal(Double.parseDouble(result.toString()) * sign); } static SfString parseSfString(Reader input) throws IOException { // It is known first character was '"' StringBuilder result = new StringBuilder(); while (true) { int c = input.read(); if (c == '\\') { requireNotChar(input, -1); c = input.read(); if (c != '\\' && c != '\"') { throw new IllegalArgumentException( sm.getString("sf.string.invalidEscape", String.format("\\u%40X", Integer.valueOf(c)))); } } else { if (c == '\"') { break; } // This test also covers unexpected EOF if (c < 32 || c > 126) { throw new IllegalArgumentException( sm.getString("sf.string.invalidCharacter", String.format("\\u%40X", Integer.valueOf(c)))); } } result.append((char) c); } return new SfString(result.toString()); } static SfToken parseSfToken(Reader input, int first) throws IOException { // It is known first character is valid StringBuilder result = new StringBuilder(); result.append((char) first); while (true) { input.mark(1); int c = input.read(); if (!isToken(c)) { input.reset(); break; } result.append((char) c); } return new SfToken(result.toString()); } static SfByteSequence parseSfByteSequence(Reader input) throws IOException { // It is known first character was ':' StringBuilder base64 = new StringBuilder(); while (true) { int c = input.read(); if (c == ':') { break; } else if (isBase64(c)) { base64.append((char) c); } else { throw new IllegalArgumentException( sm.getString("sf.base64.invalidCharacter", String.format("\\u%40X", Integer.valueOf(c)))); } } return new SfByteSequence(Base64.getDecoder().decode(base64.toString())); } static SfBoolean parseSfBoolean(Reader input) throws IOException { // It is known first character was '?' int c = input.read(); if (c == '1') { return new SfBoolean(true); } else if (c == '0') { return new SfBoolean(false); } else { throw new IllegalArgumentException( sm.getString("sf.boolean.invalidCharacter", String.format("\\u%40X", Integer.valueOf(c)))); } } static void skipSP(Reader input) throws IOException { input.mark(1); int c = input.read(); while (c == 32) { input.mark(1); c = input.read(); } input.reset(); } static void skipOWS(Reader input) throws IOException { input.mark(1); int c = input.read(); while (isOws(c)) { input.mark(1); c = input.read(); } input.reset(); } static void requireChar(Reader input, int... required) throws IOException { int c = input.read(); for (int r : required) { if (c == r) { return; } } throw new IllegalArgumentException( sm.getString("sf.invalidCharacter", String.format("\\u%40X", Integer.valueOf(c)))); } static void requireNotChar(Reader input, int required) throws IOException { input.mark(1); int c = input.read(); if (c == required) { throw new IllegalArgumentException( sm.getString("sf.invalidCharacter", String.format("\\u%40X", Integer.valueOf(c)))); } input.reset(); } static int peek(Reader input) throws IOException { input.mark(1); int c = input.read(); input.reset(); return c; } static boolean isKeyFirst(int c) { try { return IS_KEY_FIRST[c]; } catch (ArrayIndexOutOfBoundsException ex) { return false; } } static boolean isKey(int c) { try { return IS_KEY[c]; } catch (ArrayIndexOutOfBoundsException ex) { return false; } } static boolean isOws(int c) { try { return IS_OWS[c]; } catch (ArrayIndexOutOfBoundsException ex) { return false; } } static boolean isBase64(int c) { try { return IS_BASE64[c]; } catch (ArrayIndexOutOfBoundsException ex) { return false; } } static boolean isToken(int c) { try { return IS_TOKEN[c]; } catch (ArrayIndexOutOfBoundsException ex) { return false; } } private StructuredField() { // Utility class. Hide default constructor. } static class SfDictionary { private Map dictionary = new LinkedHashMap<>(); void addDictionaryMember(String key, SfListMember value) { dictionary.put(key, value); } SfListMember getDictionaryMember(String key) { return dictionary.get(key); } } static class SfList { private List listMembers = new ArrayList<>(); void addListMember(SfListMember listMember) { listMembers.add(listMember); } } static class SfListMember { private Map> parameters = null; void addParameter(String key, SfItem value) { if (parameters == null) { parameters = new LinkedHashMap<>(); } parameters.put(key, value); } } static class SfInnerList extends SfListMember { List> listItems = new ArrayList<>(); SfInnerList() { // Default constructor is NO-OP } void addListItem(SfItem item) { listItems.add(item); } List> getListItem() { return listItems; } } abstract static class SfItem extends SfListMember { private final T value; SfItem(T value) { this.value = value; } T getVaue() { return value; } } static class SfInteger extends SfItem { SfInteger(long value) { super(Long.valueOf(value)); } } static class SfDecimal extends SfItem { SfDecimal(double value) { super(Double.valueOf(value)); } } static class SfString extends SfItem { SfString(String value) { super(value); } } static class SfToken extends SfItem { SfToken(String value) { super(value); } } static class SfByteSequence extends SfItem { SfByteSequence(byte[] value) { super(value); } } static class SfBoolean extends SfItem { SfBoolean(boolean value) { super(Boolean.valueOf(value)); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy