diff -r 3b5a2cf988d2 -r 57c19149b60b jdk/src/java.httpclient/share/classes/sun/net/httpclient/hpack/Encoder.java --- a/jdk/src/java.httpclient/share/classes/sun/net/httpclient/hpack/Encoder.java Thu Dec 08 18:03:35 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,429 +0,0 @@ -/* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package sun.net.httpclient.hpack; - -import java.nio.ByteBuffer; -import java.nio.ReadOnlyBufferException; -import java.util.LinkedList; -import java.util.List; - -import static java.lang.String.format; -import static java.util.Objects.requireNonNull; - -/** - * Encodes headers to their binary representation. - * - *
Typical lifecycle looks like this: - * - *
{@link #Encoder(int) new Encoder} - * ({@link #setMaxCapacity(int) setMaxCapacity}? - * {@link #encode(ByteBuffer) encode})* - * - *
Suppose headers are represented by {@code Map Though the specification does not define how
- * an encoder is to be implemented, a default implementation is provided by the
- * method {@link #header(CharSequence, CharSequence, boolean)}.
- *
- * To provide a custom encoding implementation, {@code Encoder} has to be
- * extended. A subclass then can access methods for encoding using specific
- * representations (e.g. {@link #literal(int, CharSequence, boolean) literal},
- * {@link #indexed(int) indexed}, etc.)
- *
- * @apiNote
- *
- * An Encoder provides an incremental way of encoding headers.
- * {@link #encode(ByteBuffer)} takes a buffer a returns a boolean indicating
- * whether, or not, the buffer was sufficiently sized to hold the
- * remaining of the encoded representation.
- *
- * This way, there's no need to provide a buffer of a specific size, or to
- * resize (and copy) the buffer on demand, when the remaining encoded
- * representation will not fit in the buffer's remaining space. Instead, an
- * array of existing buffers can be used, prepended with a frame that encloses
- * the resulting header block afterwards.
- *
- * Splitting the encoding operation into header set up and header encoding,
- * separates long lived arguments ({@code name}, {@code value}, {@code
- * sensitivity}, etc.) from the short lived ones (e.g. {@code buffer}),
- * simplifying each operation itself.
- *
- * @implNote
- *
- * The default implementation does not use dynamic table. It reports to a
- * coupled Decoder a size update with the value of {@code 0}, and never changes
- * it afterwards.
- *
- * @since 9
- */
-public class Encoder {
-
- // TODO: enum: no huffman/smart huffman/always huffman
- private static final boolean DEFAULT_HUFFMAN = true;
-
- private final IndexedWriter indexedWriter = new IndexedWriter();
- private final LiteralWriter literalWriter = new LiteralWriter();
- private final LiteralNeverIndexedWriter literalNeverIndexedWriter
- = new LiteralNeverIndexedWriter();
- private final LiteralWithIndexingWriter literalWithIndexingWriter
- = new LiteralWithIndexingWriter();
- private final SizeUpdateWriter sizeUpdateWriter = new SizeUpdateWriter();
- private final BulkSizeUpdateWriter bulkSizeUpdateWriter
- = new BulkSizeUpdateWriter();
-
- private BinaryRepresentationWriter writer;
- private final HeaderTable headerTable;
-
- private boolean encoding;
-
- private int maxCapacity;
- private int currCapacity;
- private int lastCapacity;
- private long minCapacity;
- private boolean capacityUpdate;
- private boolean configuredCapacityUpdate;
-
- /**
- * Constructs an {@code Encoder} with the specified maximum capacity of the
- * header table.
- *
- * The value has to be agreed between decoder and encoder out-of-band,
- * e.g. by a protocol that uses HPACK (see 4.2. Maximum Table
- * Size).
- *
- * @param maxCapacity
- * a non-negative integer
- *
- * @throws IllegalArgumentException
- * if maxCapacity is negative
- */
- public Encoder(int maxCapacity) {
- if (maxCapacity < 0) {
- throw new IllegalArgumentException("maxCapacity >= 0: " + maxCapacity);
- }
- // Initial maximum capacity update mechanics
- minCapacity = Long.MAX_VALUE;
- currCapacity = -1;
- setMaxCapacity(maxCapacity);
- headerTable = new HeaderTable(lastCapacity);
- }
-
- /**
- * Sets up the given header {@code (name, value)}.
- *
- * Fixates {@code name} and {@code value} for the duration of encoding.
- *
- * @param name
- * the name
- * @param value
- * the value
- *
- * @throws NullPointerException
- * if any of the arguments are {@code null}
- * @throws IllegalStateException
- * if the encoder hasn't fully encoded the previous header, or
- * hasn't yet started to encode it
- * @see #header(CharSequence, CharSequence, boolean)
- */
- public void header(CharSequence name, CharSequence value)
- throws IllegalStateException {
- header(name, value, false);
- }
-
- /**
- * Sets up the given header {@code (name, value)} with possibly sensitive
- * value.
- *
- * Fixates {@code name} and {@code value} for the duration of encoding.
- *
- * @param name
- * the name
- * @param value
- * the value
- * @param sensitive
- * whether or not the value is sensitive
- *
- * @throws NullPointerException
- * if any of the arguments are {@code null}
- * @throws IllegalStateException
- * if the encoder hasn't fully encoded the previous header, or
- * hasn't yet started to encode it
- * @see #header(CharSequence, CharSequence)
- * @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean)
- */
- public void header(CharSequence name, CharSequence value,
- boolean sensitive) throws IllegalStateException {
- // Arguably a good balance between complexity of implementation and
- // efficiency of encoding
- requireNonNull(name, "name");
- requireNonNull(value, "value");
- HeaderTable t = getHeaderTable();
- int index = t.indexOf(name, value);
- if (index > 0) {
- indexed(index);
- } else if (index < 0) {
- if (sensitive) {
- literalNeverIndexed(-index, value, DEFAULT_HUFFMAN);
- } else {
- literal(-index, value, DEFAULT_HUFFMAN);
- }
- } else {
- if (sensitive) {
- literalNeverIndexed(name, DEFAULT_HUFFMAN, value, DEFAULT_HUFFMAN);
- } else {
- literal(name, DEFAULT_HUFFMAN, value, DEFAULT_HUFFMAN);
- }
- }
- }
-
- /**
- * Sets a maximum capacity of the header table.
- *
- * The value has to be agreed between decoder and encoder out-of-band,
- * e.g. by a protocol that uses HPACK (see 4.2. Maximum Table
- * Size).
- *
- * May be called any number of times after or before a complete header
- * has been encoded.
- *
- * If the encoder decides to change the actual capacity, an update will
- * be encoded before a new encoding operation starts.
- *
- * @param capacity
- * a non-negative integer
- *
- * @throws IllegalArgumentException
- * if capacity is negative
- * @throws IllegalStateException
- * if the encoder hasn't fully encoded the previous header, or
- * hasn't yet started to encode it
- */
- public void setMaxCapacity(int capacity) {
- checkEncoding();
- if (capacity < 0) {
- throw new IllegalArgumentException("capacity >= 0: " + capacity);
- }
- int calculated = calculateCapacity(capacity);
- if (calculated < 0 || calculated > capacity) {
- throw new IllegalArgumentException(
- format("0 <= calculated <= capacity: calculated=%s, capacity=%s",
- calculated, capacity));
- }
- capacityUpdate = true;
- // maxCapacity needs to be updated unconditionally, so the encoder
- // always has the newest one (in case it decides to update it later
- // unsolicitedly)
- // Suppose maxCapacity = 4096, and the encoder has decided to use only
- // 2048. It later can choose anything else from the region [0, 4096].
- maxCapacity = capacity;
- lastCapacity = calculated;
- minCapacity = Math.min(minCapacity, lastCapacity);
- }
-
- protected int calculateCapacity(int maxCapacity) {
- // Default implementation of the Encoder won't add anything to the
- // table, therefore no need for a table space
- return 0;
- }
-
- /**
- * Encodes the {@linkplain #header(CharSequence, CharSequence) set up}
- * header into the given buffer.
- *
- * The encoder writes as much as possible of the header's binary
- * representation into the given buffer, starting at the buffer's position,
- * and increments its position to reflect the bytes written. The buffer's
- * mark and limit will not be modified.
- *
- * Once the method has returned {@code true}, the current header is
- * deemed encoded. A new header may be set up.
- *
- * @param headerBlock
- * the buffer to encode the header into, may be empty
- *
- * @return {@code true} if the current header has been fully encoded,
- * {@code false} otherwise
- *
- * @throws NullPointerException
- * if the buffer is {@code null}
- * @throws ReadOnlyBufferException
- * if this buffer is read-only
- * @throws IllegalStateException
- * if there is no set up header
- */
- public final boolean encode(ByteBuffer headerBlock) {
- if (!encoding) {
- throw new IllegalStateException("A header hasn't been set up");
- }
- if (!prependWithCapacityUpdate(headerBlock)) {
- return false;
- }
- boolean done = writer.write(headerTable, headerBlock);
- if (done) {
- writer.reset(); // FIXME: WHY?
- encoding = false;
- }
- return done;
- }
-
- private boolean prependWithCapacityUpdate(ByteBuffer headerBlock) {
- if (capacityUpdate) {
- if (!configuredCapacityUpdate) {
- List{@code
- * for (Map.Entry
- *
- *