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