//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.http;

Implements a quoted comma separated list parser in accordance with RFC7230. OWS is removed and quoted characters ignored for parsing.
See Also:
  • https://tools.ietf.org/html/rfc7230#section-3.2.6
  • https://tools.ietf.org/html/rfc7230#section-7
/** * Implements a quoted comma separated list parser * in accordance with RFC7230. * OWS is removed and quoted characters ignored for parsing. * * @see "https://tools.ietf.org/html/rfc7230#section-3.2.6" * @see "https://tools.ietf.org/html/rfc7230#section-7" */
public abstract class QuotedCSVParser { private enum State { VALUE, PARAM_NAME, PARAM_VALUE } protected final boolean _keepQuotes; public QuotedCSVParser(boolean keepQuotes) { _keepQuotes = keepQuotes; } public static String unquote(String s) { // handle trivial cases int l = s.length(); if (s == null || l == 0) return s; // Look for any quotes int i = 0; for (; i < l; i++) { char c = s.charAt(i); if (c == '"') break; } if (i == l) return s; boolean quoted = true; boolean sloshed = false; StringBuffer buffer = new StringBuffer(); buffer.append(s, 0, i); i++; for (; i < l; i++) { char c = s.charAt(i); if (quoted) { if (sloshed) { buffer.append(c); sloshed = false; } else if (c == '"') quoted = false; else if (c == '\\') sloshed = true; else buffer.append(c); } else if (c == '"') quoted = true; else buffer.append(c); } return buffer.toString(); }
Add and parse a value string(s)
Params:
  • value – A value that may contain one or more Quoted CSV items.
/** * Add and parse a value string(s) * * @param value A value that may contain one or more Quoted CSV items. */
public void addValue(String value) { if (value == null) return; StringBuffer buffer = new StringBuffer(); int l = value.length(); State state = State.VALUE; boolean quoted = false; boolean sloshed = false; int nwsLength = 0; int lastLength = 0; int valueLength = -1; int paramName = -1; int paramValue = -1; for (int i = 0; i <= l; i++) { char c = i == l ? 0 : value.charAt(i); // Handle quoting https://tools.ietf.org/html/rfc7230#section-3.2.6 if (quoted && c != 0) { if (sloshed) sloshed = false; else { switch (c) { case '\\': sloshed = true; if (!_keepQuotes) continue; break; case '"': quoted = false; if (!_keepQuotes) continue; break; default: break; } } buffer.append(c); nwsLength = buffer.length(); continue; } // Handle common cases switch (c) { case ' ': case '\t': if (buffer.length() > lastLength) // not leading OWS buffer.append(c); continue; case '"': quoted = true; if (_keepQuotes) { if (state == State.PARAM_VALUE && paramValue < 0) paramValue = nwsLength; buffer.append(c); } else if (state == State.PARAM_VALUE && paramValue < 0) paramValue = nwsLength; nwsLength = buffer.length(); continue; case ';': buffer.setLength(nwsLength); // trim following OWS if (state == State.VALUE) { parsedValue(buffer); valueLength = buffer.length(); } else parsedParam(buffer, valueLength, paramName, paramValue); nwsLength = buffer.length(); paramName = paramValue = -1; buffer.append(c); lastLength = ++nwsLength; state = State.PARAM_NAME; continue; case ',': case 0: if (nwsLength > 0) { buffer.setLength(nwsLength); // trim following OWS switch (state) { case VALUE: parsedValue(buffer); valueLength = buffer.length(); break; case PARAM_NAME: case PARAM_VALUE: parsedParam(buffer, valueLength, paramName, paramValue); break; default: throw new IllegalStateException(state.toString()); } parsedValueAndParams(buffer); } buffer.setLength(0); lastLength = 0; nwsLength = 0; valueLength = paramName = paramValue = -1; state = State.VALUE; continue; case '=': switch (state) { case VALUE: // It wasn't really a value, it was a param name paramName = 0; buffer.setLength(nwsLength); // trim following OWS final String param = buffer.toString(); buffer.setLength(0); parsedValue(buffer); valueLength = buffer.length(); buffer.append(param); buffer.append(c); lastLength = ++nwsLength; state = State.PARAM_VALUE; continue; case PARAM_NAME: buffer.setLength(nwsLength); // trim following OWS buffer.append(c); lastLength = ++nwsLength; state = State.PARAM_VALUE; continue; case PARAM_VALUE: if (paramValue < 0) paramValue = nwsLength; buffer.append(c); nwsLength = buffer.length(); continue; default: throw new IllegalStateException(state.toString()); } default: { switch (state) { case VALUE: { buffer.append(c); nwsLength = buffer.length(); continue; } case PARAM_NAME: { if (paramName < 0) paramName = nwsLength; buffer.append(c); nwsLength = buffer.length(); continue; } case PARAM_VALUE: { if (paramValue < 0) paramValue = nwsLength; buffer.append(c); nwsLength = buffer.length(); continue; } default: throw new IllegalStateException(state.toString()); } } } } }
Called when a value and it's parameters has been parsed
Params:
  • buffer – Containing the trimmed value and parameters
/** * Called when a value and it's parameters has been parsed * * @param buffer Containing the trimmed value and parameters */
protected void parsedValueAndParams(StringBuffer buffer) { }
Called when a value has been parsed (prior to any parameters)
Params:
  • buffer – Containing the trimmed value, which may be mutated
/** * Called when a value has been parsed (prior to any parameters) * * @param buffer Containing the trimmed value, which may be mutated */
protected void parsedValue(StringBuffer buffer) { }
Called when a parameter has been parsed
Params:
  • buffer – Containing the trimmed value and all parameters, which may be mutated
  • valueLength – The length of the value
  • paramName – The index of the start of the parameter just parsed
  • paramValue – The index of the start of the parameter value just parsed, or -1
/** * Called when a parameter has been parsed * * @param buffer Containing the trimmed value and all parameters, which may be mutated * @param valueLength The length of the value * @param paramName The index of the start of the parameter just parsed * @param paramValue The index of the start of the parameter value just parsed, or -1 */
protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue) { } }