/* * 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.coyote.http2;
/** * Encoder for HPACK frames.
*/ class HpackEncoder {
privatestaticfinal Log log = LogFactory.getLog(HpackEncoder.class); privatestaticfinal StringManager sm = StringManager.getManager(HpackEncoder.class);
privatestaticfinal HpackHeaderFunction DEFAULT_HEADER_FUNCTION = new HpackHeaderFunction() {
@Override publicboolean shouldUseIndexing(String headerName, String value) { // content length and date change all the time // no need to index them, or they will churn the table switch (headerName) { case"content-length": case"date": returnfalse; default: returntrue;
}
}
@Override publicboolean shouldUseHuffman(String header, String value) { return value.length() > 5; // TODO: figure out a good value for this
}
@Override publicboolean shouldUseHuffman(String header) { return header.length() > 5; // TODO: figure out a good value for this
}
privateint newMaxHeaderSize = -1; // if the max header size has been changed privateint minNewMaxHeaderSize = -1; // records the smallest value of newMaxHeaderSize, as per section 4.1
privatefinal Deque<TableEntry> evictionQueue = new ArrayDeque<>(); privatefinal Map<String,List<TableEntry>> dynamicTable = new HashMap<>(); // TODO: use a custom data structure to // reduce allocations
static {
Map<String,TableEntry[]> map = new HashMap<>(); for (int i = 1; i < Hpack.STATIC_TABLE.length; ++i) {
Hpack.HeaderField m = Hpack.STATIC_TABLE[i];
TableEntry[] existing = map.get(m.name); if (existing == null) {
map.put(m.name, new TableEntry[] { new TableEntry(m.name, m.value, i) });
} else {
TableEntry[] newEntry = new TableEntry[existing.length + 1];
System.arraycopy(existing, 0, newEntry, 0, existing.length);
newEntry[existing.length] = new TableEntry(m.name, m.value, i);
map.put(m.name, newEntry);
}
}
ENCODING_STATIC_TABLE = Collections.unmodifiableMap(map);
}
/** * The maximum table size
*/ privateint maxTableSize = Hpack.DEFAULT_TABLE_SIZE;
/** * The current table size
*/ privateint currentTableSize;
/** * Encodes the headers into a buffer. * * @param headers The headers to encode * @param target The buffer to which to write the encoded headers * * @return The state of the encoding process
*/
State encode(MimeHeaders headers, ByteBuffer target) { int it = headersIterator; if (headersIterator == -1) {
handleTableSizeChange(target); // new headers map
it = 0;
currentHeaders = headers;
} else { if (headers != currentHeaders) { thrownew IllegalStateException();
}
} while (it < currentHeaders.size()) { // FIXME: Review lowercase policy
String headerName = headers.getName(it).toString().toLowerCase(Locale.US); boolean skip = false; if (firstPass) { if (headerName.charAt(0) != ':') {
skip = true;
}
} else { if (headerName.charAt(0) == ':') {
skip = true;
}
} if (!skip) {
String val = headers.getValue(it).toString();
privatevoid writeHuffmanEncodableName(ByteBuffer target, String headerName) { if (hpackHeaderFunction.shouldUseHuffman(headerName)) { if (HPackHuffman.encode(target, headerName, true)) { return;
}
}
target.put((byte) 0); // to use encodeInteger we need to place the first byte in the buffer.
Hpack.encodeInteger(target, headerName.length(), 7); for (int j = 0; j < headerName.length(); ++j) {
target.put((byte) Hpack.toLower(headerName.charAt(j)));
}
privatevoid writeValueString(ByteBuffer target, String val) {
target.put((byte) 0); // to use encodeInteger we need to place the first byte in the buffer.
Hpack.encodeInteger(target, val.length(), 7); for (int j = 0; j < val.length(); ++j) {
target.put((byte) val.charAt(j));
}
}
privatevoid addToDynamicTable(String headerName, String val) { int pos = entryPositionCounter++;
DynamicTableEntry d = new DynamicTableEntry(headerName, val, -pos);
dynamicTable.computeIfAbsent(headerName, k -> new ArrayList<>(1)).add(d);
evictionQueue.add(d);
currentTableSize += d.getSize();
runEvictionIfRequired(); if (entryPositionCounter == Integer.MAX_VALUE) { // prevent rollover
preventPositionRollover();
}
}
privatevoid preventPositionRollover() { // if the position counter is about to roll over we iterate all the table entries // and set their position to their actual position for (List<TableEntry> tableEntries : dynamicTable.values()) { for (TableEntry t : tableEntries) {
t.position = t.getPosition();
}
}
entryPositionCounter = 0;
}
privatevoid runEvictionIfRequired() {
while (currentTableSize > maxTableSize) {
TableEntry next = evictionQueue.poll(); if (next == null) { return;
}
currentTableSize -= next.size;
List<TableEntry> list = dynamicTable.get(next.name);
list.remove(next); if (list.isEmpty()) {
dynamicTable.remove(next.name);
}
}
}
private TableEntry findInTable(String headerName, String value) {
TableEntry[] staticTable = ENCODING_STATIC_TABLE.get(headerName); if (staticTable != null) { for (TableEntry st : staticTable) { if (st.value != null && st.value.equals(value)) { // todo: some form of lookup? return st;
}
}
}
List<TableEntry> dynamic = dynamicTable.get(headerName); if (dynamic != null) { for (TableEntry st : dynamic) { if (st.value.equals(value)) { // todo: some form of lookup? return st;
}
}
} if (staticTable != null) { return staticTable[0];
} returnnull;
}
/** * Returns true if huffman encoding should be used on the header value * * @param header The header name * @param value The header value to be encoded * * @return <code>true</code> if the value should be encoded
*/ boolean shouldUseHuffman(String header, String value);
/** * Returns true if huffman encoding should be used on the header name * * @param header The header name to be encoded * * @return <code>true</code> if the value should be encoded
*/ boolean shouldUseHuffman(String header);
}
}
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.