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

io.netty.handler.codec.http.ReadOnlyHttpHeaders Maven / Gradle / Ivy

/*
 * Copyright 2017 The Netty Project
 *
 * The Netty Project 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:
 *
 *   https://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.netty.handler.codec.http;

import io.netty.util.AsciiString;
import io.netty.util.internal.UnstableApi;

import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

import static io.netty.handler.codec.CharSequenceValueConverter.INSTANCE;
import static io.netty.handler.codec.http.DefaultHttpHeaders.HttpNameValidator;
import static io.netty.util.AsciiString.contentEquals;
import static io.netty.util.AsciiString.contentEqualsIgnoreCase;

/**
 * A variant of {@link HttpHeaders} which only supports read-only methods.
 * 

* Any array passed to this class may be used directly in the underlying data structures of this class. If these * arrays may be modified it is the caller's responsibility to supply this class with a copy of the array. *

* This may be a good alternative to {@link DefaultHttpHeaders} if your have a fixed set of headers which will not * change. */ @UnstableApi public final class ReadOnlyHttpHeaders extends HttpHeaders { private final CharSequence[] nameValuePairs; /** * Create a new instance. * @param validateHeaders {@code true} to validate the contents of each header name. * @param nameValuePairs An array of the structure {@code [,,...]}. * A copy will NOT be made of this array. If the contents of this array * may be modified externally you are responsible for passing in a copy. */ public ReadOnlyHttpHeaders(boolean validateHeaders, CharSequence... nameValuePairs) { if ((nameValuePairs.length & 1) != 0) { throw newInvalidArraySizeException(); } if (validateHeaders) { validateHeaders(nameValuePairs); } this.nameValuePairs = nameValuePairs; } private static IllegalArgumentException newInvalidArraySizeException() { return new IllegalArgumentException("nameValuePairs must be arrays of [name, value] pairs"); } private static void validateHeaders(CharSequence... keyValuePairs) { for (int i = 0; i < keyValuePairs.length; i += 2) { HttpNameValidator.validateName(keyValuePairs[i]); } } private CharSequence get0(CharSequence name) { final int nameHash = AsciiString.hashCode(name); for (int i = 0; i < nameValuePairs.length; i += 2) { CharSequence roName = nameValuePairs[i]; if (AsciiString.hashCode(roName) == nameHash && contentEqualsIgnoreCase(roName, name)) { // Suppress a warning out of bounds access since the constructor allows only pairs return nameValuePairs[i + 1]; } } return null; } @Override public String get(String name) { CharSequence value = get0(name); return value == null ? null : value.toString(); } @Override public Integer getInt(CharSequence name) { CharSequence value = get0(name); return value == null ? null : INSTANCE.convertToInt(value); } @Override public int getInt(CharSequence name, int defaultValue) { CharSequence value = get0(name); return value == null ? defaultValue : INSTANCE.convertToInt(value); } @Override public Short getShort(CharSequence name) { CharSequence value = get0(name); return value == null ? null : INSTANCE.convertToShort(value); } @Override public short getShort(CharSequence name, short defaultValue) { CharSequence value = get0(name); return value == null ? defaultValue : INSTANCE.convertToShort(value); } @Override public Long getTimeMillis(CharSequence name) { CharSequence value = get0(name); return value == null ? null : INSTANCE.convertToTimeMillis(value); } @Override public long getTimeMillis(CharSequence name, long defaultValue) { CharSequence value = get0(name); return value == null ? defaultValue : INSTANCE.convertToTimeMillis(value); } @Override public List getAll(String name) { if (isEmpty()) { return Collections.emptyList(); } final int nameHash = AsciiString.hashCode(name); List values = new ArrayList(4); for (int i = 0; i < nameValuePairs.length; i += 2) { CharSequence roName = nameValuePairs[i]; if (AsciiString.hashCode(roName) == nameHash && contentEqualsIgnoreCase(roName, name)) { values.add(nameValuePairs[i + 1].toString()); } } return values; } @Override public List> entries() { if (isEmpty()) { return Collections.emptyList(); } List> entries = new ArrayList>(size()); for (int i = 0; i < nameValuePairs.length; i += 2) { entries.add(new SimpleImmutableEntry(nameValuePairs[i].toString(), nameValuePairs[i + 1].toString())); // [java/index-out-of-bounds] } return entries; } @Override public boolean contains(String name) { return get0(name) != null; } @Override public boolean contains(String name, String value, boolean ignoreCase) { return containsValue(name, value, ignoreCase); } @Override public boolean containsValue(CharSequence name, CharSequence value, boolean ignoreCase) { if (ignoreCase) { for (int i = 0; i < nameValuePairs.length; i += 2) { if (contentEqualsIgnoreCase(nameValuePairs[i], name) && contentEqualsIgnoreCase(nameValuePairs[i + 1], value)) { return true; } } } else { for (int i = 0; i < nameValuePairs.length; i += 2) { if (contentEqualsIgnoreCase(nameValuePairs[i], name) && contentEquals(nameValuePairs[i + 1], value)) { return true; } } } return false; } @Override public Iterator valueStringIterator(CharSequence name) { return new ReadOnlyStringValueIterator(name); } @Override public Iterator valueCharSequenceIterator(CharSequence name) { return new ReadOnlyValueIterator(name); } @Override public Iterator> iterator() { return new ReadOnlyStringIterator(); } @Override public Iterator> iteratorCharSequence() { return new ReadOnlyIterator(); } @Override public boolean isEmpty() { return nameValuePairs.length == 0; } @Override public int size() { return nameValuePairs.length >>> 1; } @Override public Set names() { if (isEmpty()) { return Collections.emptySet(); } Set names = new LinkedHashSet(size()); for (int i = 0; i < nameValuePairs.length; i += 2) { names.add(nameValuePairs[i].toString()); } return names; } @Override public HttpHeaders add(String name, Object value) { throw new UnsupportedOperationException("read only"); } @Override public HttpHeaders add(String name, Iterable values) { throw new UnsupportedOperationException("read only"); } @Override public HttpHeaders addInt(CharSequence name, int value) { throw new UnsupportedOperationException("read only"); } @Override public HttpHeaders addShort(CharSequence name, short value) { throw new UnsupportedOperationException("read only"); } @Override public HttpHeaders set(String name, Object value) { throw new UnsupportedOperationException("read only"); } @Override public HttpHeaders set(String name, Iterable values) { throw new UnsupportedOperationException("read only"); } @Override public HttpHeaders setInt(CharSequence name, int value) { throw new UnsupportedOperationException("read only"); } @Override public HttpHeaders setShort(CharSequence name, short value) { throw new UnsupportedOperationException("read only"); } @Override public HttpHeaders remove(String name) { throw new UnsupportedOperationException("read only"); } @Override public HttpHeaders clear() { throw new UnsupportedOperationException("read only"); } private final class ReadOnlyIterator implements Map.Entry, Iterator> { private CharSequence key; private CharSequence value; private int nextNameIndex; @Override public boolean hasNext() { return nextNameIndex != nameValuePairs.length; } @Override public Map.Entry next() { if (!hasNext()) { throw new NoSuchElementException(); } key = nameValuePairs[nextNameIndex]; value = nameValuePairs[nextNameIndex + 1]; nextNameIndex += 2; return this; } @Override public void remove() { throw new UnsupportedOperationException("read only"); } @Override public CharSequence getKey() { return key; } @Override public CharSequence getValue() { return value; } @Override public CharSequence setValue(CharSequence value) { throw new UnsupportedOperationException("read only"); } @Override public String toString() { return key.toString() + '=' + value.toString(); } } private final class ReadOnlyStringIterator implements Map.Entry, Iterator> { private String key; private String value; private int nextNameIndex; @Override public boolean hasNext() { return nextNameIndex != nameValuePairs.length; } @Override public Map.Entry next() { if (!hasNext()) { throw new NoSuchElementException(); } key = nameValuePairs[nextNameIndex].toString(); value = nameValuePairs[nextNameIndex + 1].toString(); nextNameIndex += 2; return this; } @Override public void remove() { throw new UnsupportedOperationException("read only"); } @Override public String getKey() { return key; } @Override public String getValue() { return value; } @Override public String setValue(String value) { throw new UnsupportedOperationException("read only"); } @Override public String toString() { return key + '=' + value; } } private final class ReadOnlyStringValueIterator implements Iterator { private final CharSequence name; private final int nameHash; private int nextNameIndex; ReadOnlyStringValueIterator(CharSequence name) { this.name = name; nameHash = AsciiString.hashCode(name); nextNameIndex = findNextValue(); } @Override public boolean hasNext() { return nextNameIndex != -1; } @Override public String next() { if (!hasNext()) { throw new NoSuchElementException(); } String value = nameValuePairs[nextNameIndex + 1].toString(); nextNameIndex = findNextValue(); return value; } @Override public void remove() { throw new UnsupportedOperationException("read only"); } private int findNextValue() { for (int i = nextNameIndex; i < nameValuePairs.length; i += 2) { final CharSequence roName = nameValuePairs[i]; if (nameHash == AsciiString.hashCode(roName) && contentEqualsIgnoreCase(name, roName)) { return i; } } return -1; } } private final class ReadOnlyValueIterator implements Iterator { private final CharSequence name; private final int nameHash; private int nextNameIndex; ReadOnlyValueIterator(CharSequence name) { this.name = name; nameHash = AsciiString.hashCode(name); nextNameIndex = findNextValue(); } @Override public boolean hasNext() { return nextNameIndex != -1; } @Override public CharSequence next() { if (!hasNext()) { throw new NoSuchElementException(); } CharSequence value = nameValuePairs[nextNameIndex + 1]; nextNameIndex = findNextValue(); return value; } @Override public void remove() { throw new UnsupportedOperationException("read only"); } private int findNextValue() { for (int i = nextNameIndex; i < nameValuePairs.length; i += 2) { final CharSequence roName = nameValuePairs[i]; if (nameHash == AsciiString.hashCode(roName) && contentEqualsIgnoreCase(name, roName)) { return i; } } return -1; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy