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

com.caucho.v5.http.protocol2.InHeader Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
/*
 * Copyright (c) 1998-2015 Caucho Technology -- all rights reserved
 *
 * This file is part of Baratine(TM)
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Baratine is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Baratine 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Baratine; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.v5.http.protocol2;

import java.io.IOException;
import java.util.HashMap;
import java.util.Objects;

import com.caucho.v5.io.ReadBuffer;
import com.caucho.v5.util.L10N;


/**
 * HeaderOut is the compression for the writer.
 */
public class InHeader extends HeaderCommon implements AutoCloseable
{
  private static final L10N L = new L10N(InHeader.class);
  
  private HashMap _tableKeyMap;
  private HashMap _tableEntryMap;
    
  private int _tableCapacity = 4096;
  private int _tableSize = 0;
  
  private TableEntry []_entries;
  
  private TableEntry _key = new TableEntry();
  
  private ReadBuffer _is;

  private StateHeaderIn _state;
  
  private char []_charBuffer = new char[256];
  
  private int _length;
  
  private int _streamId;
  
  private long _sequenceHead;
  private long _sequenceTail;
  
  private long _sequenceReference;

  private int _pad;
  
  public InHeader(ReadBuffer is)
  {
    Objects.requireNonNull(is);
    
    _tableKeyMap = new HashMap<>();
    _tableKeyMap.putAll(getTableKeyStatic());
    
    _tableEntryMap = new HashMap<>();
    _tableEntryMap.putAll(getTableEntryStatic());
    
    _entries = new TableEntry[256];
    
    _is = is;
  }
  
  boolean readHeaders(InRequest request, int length, int flags)
    throws IOException
  {
    _length = length;
    
    int pad = 0;
    
    if ((flags & Http2Constants.PAD_HIGH) != 0) {
      int padHigh = read();
      int padLow = read();
      
      flags &= ~(Http2Constants.PAD_HIGH|Http2Constants.PAD_LOW);

      pad = (padHigh * 256 + padLow);
    }
    else if ((flags & Http2Constants.PAD_LOW) != 0) {
      int padLow = read();
      
      flags &= ~Http2Constants.PAD_LOW;
      
      pad = padLow;
    }
    
    if ((flags & Http2Constants.PRIORITY) != 0) {
      int depend = readInt();
      int weight = read();
      
      flags &= ~Http2Constants.PRIORITY;
    }
    
    _length -= pad;
    
    switch (flags) {
    case 0:
      _state = StateHeaderIn.HEADER_CONT_STREAM_CONT;
      break;

    case Http2Constants.END_STREAM:
      _state = StateHeaderIn.HEADER_CONT_STREAM_END;
      break;

    case Http2Constants.END_HEADERS:
      _state = StateHeaderIn.HEADER_END_STREAM_CONT;
      break;
      
    case Http2Constants.END_STREAM|Http2Constants.END_HEADERS:
      _state = StateHeaderIn.HEADER_END_STREAM_END;
      break;
      
    default:
      throw new IllegalStateException(L.l("Invalid header flags 0x{0}",
                                          Integer.toHexString(flags)));
    }
    
    long seqReference = ++_sequenceReference;
    
    startHeaders(request);
    
    readHeaders(request, seqReference);
    
    completeHeaders(request, seqReference);
    
    skip(pad);
    
    return true;
  }
  
  private void startHeaders(InRequest request)
  {
  }
  
  private void readHeaders(InRequest request, long seqReference)
    throws IOException
  {
    int op;
    
    while ((op = read()) >= 0) {
      if ((op & 0x80) != 0) {
        readIndex(request, op);
      }
      else if ((op & 0x40) != 0) {
        readHeader(request, 6, op, true, seqReference);
      }
      else if ((op & 0x30) == 0x30) {
        clearReferenceSet();
      }
      else if ((op & 0x30) == 0x20) {
        int capacity = readInt(4, op);
        
        _tableCapacity = capacity;
        
        updateTableSize();
      }
      else if ((op & 0x10) != 0) {
        readHeader(request, 4, op, false, seqReference);
      }
      else {
        readHeader(request, 4, op, false, seqReference);
      }
    }
  }
  
  private void readHeader(InRequest request,
                          int bits,
                          int op, 
                          boolean isUpdateTable,
                          long seqReference)
    throws IOException
  {
    String key;
    
    int mask = (1 << bits) - 1;
    
    if ((op & mask) != 0) {
      int index = readInt(bits, op);
      
      key = readKeyHeader(index);
    }
    else {
      key = readString();
    }
    
    String value = readString();
    
    request.header(key, value);
    
    if (isUpdateTable) {
      TableEntry entry = new TableEntry();
      
      entry.update(0, key, value);
      
      addEntry(entry);
    }
  }

  private void addEntry(TableEntry entry)
  {
    long head = _sequenceHead;
    entry.setSequence(head);
    entry.setReference(_sequenceReference + 1);

    _sequenceHead = head + 1;

    _tableEntryMap.put(entry, entry);

    if (entry.getNext() == null) {
      _tableKeyMap.put(entry.getKey(), entry);
    }

    _entries[(int) (head % _entries.length)] = entry;

    _tableSize += entry.getSize();

    updateTableSize();
  }
  
  private void clearReferenceSet()
  {
    for (long i = _sequenceTail; i < _sequenceHead; i++) {
      TableEntry entry = _entries[(int) (i % _entries.length)];
      
      entry.setReference(0);
    }
  }
  
  private void completeHeaders(InRequest request,
                               long reference)
  {
    for (long ptr = _sequenceTail; ptr < _sequenceHead; ptr++) {
      TableEntry entry = _entries[(int) (ptr % _entries.length)];
      
      if (entry.getReference() == reference) {
        entry.setReference(reference + 1);
        request.header(entry.getKey(), entry.getValue());
      }
    }
  }
  
  private void readIndex(InRequest request, int op)
    throws IOException
  {
    int index = readInt(7, op);
    
    TableEntry entry = getEntry(index);
    
    if (entry.getReference() != _sequenceReference) {
      request.header(entry.getKey(), entry.getValue());
      
      if (entry.isStatic()) {
        TableEntry next = new TableEntry(entry);
        
        addEntry(next);
      }
    }
    else {
      // index entries already in the reference is a deletion.
      entry.setReference(0);
    }
  }
  
  private String readKeyHeader(int index)
    throws IOException
  {
    TableEntry entry = getEntry(index);

    return entry.getKey();
  }
  
  private TableEntry getEntry(int index)
  {
    int length = (int) (_sequenceHead - _sequenceTail);
    
    if (length < index) {
      index -= length;

      TableEntry []entryArray = getEntryArrayStatic();
      
      if (index == 0 || entryArray.length <= index) {
        throw new Http2ProtocolException(L.l("Invalid header index '{0}'", index));
      }
    
      TableEntry entry = entryArray[index];
      
      return entry;
    }
    else {
      TableEntry []entryArray = _entries;
      
      long i = _sequenceHead - index;
      
      TableEntry entry = entryArray[(int) (i % entryArray.length)];
      
      return entry;
    }
  }
  
  private void updateTableSize()
  {
    while (_tableCapacity < _tableSize) {
      removeTableEntry();
    }
  }
  
  private void removeTableEntry()
  {
    long i = _sequenceTail++;
    
    TableEntry entry = _entries[(int) (i % _entries.length)];
    entry.setReference(0);
    
    _tableEntryMap.remove(entry);
    
    TableEntry next = entry.getNext();
    if (next != null) {
      _tableEntryMap.put(next, next);
    }
    
    _tableSize -= entry.getSize();
  }
  
  private int readInt(int bits, int d)
    throws IOException
  {
    int mask = (1 << bits) - 1;
    
    int value = d & mask;
    
    if (value != mask) {
      return value;
    }
    
    int m = 0;
    
    do {
      d = read();
    
      value += (d & 0x7f) << m;
      
      m += 7;
    } while ((d & 0x80) == 0x80);
    
    return value;
  }
 
  private String readString()
    throws IOException
  {
    int len = read();
    
    char []buffer = _charBuffer;
    
    if ((len & 0x80) != 0) {
      // huffman encoded
      len &= 0x7f;
      
      while (buffer.length <= 2 * len) {
        buffer = new char[2 * buffer.length];
        _charBuffer = buffer;
      }
      
      int strlen = huffmanDecode(len, buffer);
      
      return new String(buffer, 0, strlen);
    }
    else {
      while (buffer.length <= len) {
        buffer = new char[2 * buffer.length];
        _charBuffer = buffer;
      }
      
      for (int i = 0; i < len; i++) {
        buffer[i] = (char) read();
      }
      
      return new String(buffer, 0, len);
    }
    
  }
  
  protected int readInt()
    throws IOException
  {
    return (((read() & 0xff) << 24)
           | ((read() & 0xff) << 16)
           | ((read() & 0xff) << 8)
           | ((read() & 0xff)));
  }
  
  protected void skip(int n)
    throws IOException
  {
    _length = n;
    
    for (int i = 0; i < n; i++) {
      read();
    }
  }
  
  @Override
  protected int read()
    throws IOException
  {
    int length = _length;
    
    if (length <= 0) {
      return -1;
    }
    
    length--;
    
    int ch = _is.read();
    
    _length = length;
    
    return ch;
  }
  
  @Override
  public void close()
  {
    //_os.write(_buffer, 0, _index);
  }
  
  static enum StateHeaderIn {
    HEADER_CONT_STREAM_CONT {
      boolean isEndStream() { return false; }
    },
    HEADER_CONT_STREAM_END {
      boolean isEndStream() { return true; }
    },
    HEADER_END_STREAM_END {
      boolean isEndStream() { return true; }
    },
    HEADER_END_STREAM_CONT {
      boolean isEndStream() { return false; }
    };
    
    abstract boolean isEndStream();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy