1e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller/*
2e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Copyright (C) 2013 Square, Inc.
3e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *
4e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License");
5e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * you may not use this file except in compliance with the License.
6e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * You may obtain a copy of the License at
7e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *
8e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *      http://www.apache.org/licenses/LICENSE-2.0
9e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *
10e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Unless required by applicable law or agreed to in writing, software
11e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS,
12e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * See the License for the specific language governing permissions and
14e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * limitations under the License.
15e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */
1671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerpackage com.squareup.okhttp.internal.framed;
173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.IOException;
193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.ArrayList;
203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.List;
213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.zip.DataFormatException;
223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.zip.Inflater;
23e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Buffer;
243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.BufferedSource;
253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.ByteString;
26e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.ForwardingSource;
273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.InflaterSource;
283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Okio;
293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Source;
303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/**
323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Reads a SPDY/3 Name/Value header block. This class is made complicated by the
333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * requirement that we're strict with which bytes we put in the compressed bytes
343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * buffer. We need to put all compressed bytes into that buffer -- but no other
353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * bytes.
363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */
373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerclass NameValueBlockReader {
383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** This source transforms compressed bytes into uncompressed bytes. */
393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final InflaterSource inflaterSource;
403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * How many compressed bytes must be read into inflaterSource before
433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * {@link #readNameValueBlock} returns.
443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private int compressedLimit;
463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** This source holds inflated bytes. */
483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final BufferedSource source;
493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
50e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  public NameValueBlockReader(BufferedSource source) {
513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Limit the inflater input stream to only those bytes in the Name/Value
523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // block. We cut the inflater off at its source because we can't predict the
533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // ratio of compressed bytes to uncompressed bytes.
54e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Source throttleSource = new ForwardingSource(source) {
55e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public long read(Buffer sink, long byteCount) throws IOException {
563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (compressedLimit == 0) return -1; // Out of data for the current block.
57e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        long read = super.read(sink, Math.min(byteCount, compressedLimit));
583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (read == -1) return -1;
593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        compressedLimit -= read;
603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return read;
613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    };
633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Subclass inflater to install a dictionary when it's needed.
653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Inflater inflater = new Inflater() {
663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      @Override public int inflate(byte[] buffer, int offset, int count)
673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          throws DataFormatException {
683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        int result = super.inflate(buffer, offset, count);
693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (result == 0 && needsDictionary()) {
703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          setDictionary(Spdy3.DICTIONARY);
713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          result = super.inflate(buffer, offset, count);
723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return result;
743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    };
763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.inflaterSource = new InflaterSource(throttleSource, inflater);
783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.source = Okio.buffer(inflaterSource);
793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public List<Header> readNameValueBlock(int length) throws IOException {
823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.compressedLimit += length;
833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int numberOfPairs = source.readInt();
853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (numberOfPairs < 0) throw new IOException("numberOfPairs < 0: " + numberOfPairs);
863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (numberOfPairs > 1024) throw new IOException("numberOfPairs > 1024: " + numberOfPairs);
873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
88e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    List<Header> entries = new ArrayList<>(numberOfPairs);
893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    for (int i = 0; i < numberOfPairs; i++) {
903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      ByteString name = readByteString().toAsciiLowercase();
913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      ByteString values = readByteString();
923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (name.size() == 0) throw new IOException("name.size == 0");
933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      entries.add(new Header(name, values));
943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    doneReading();
973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return entries;
983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private ByteString readByteString() throws IOException {
1013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int length = source.readInt();
1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return source.readByteString(length);
1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private void doneReading() throws IOException {
1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Move any outstanding unread bytes into the inflater. One side-effect of
1073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // deflate compression is that sometimes there are bytes remaining in the
1083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // stream after we've consumed all of the content.
1093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (compressedLimit > 0) {
1103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      inflaterSource.refill();
1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (compressedLimit != 0) throw new IOException("compressedLimit > 0: " + compressedLimit);
1123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public void close() throws IOException {
1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    source.close();
1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller}
119