View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.http2.hpack;
29  
30  import java.util.HashMap;
31  import java.util.LinkedList;
32  import java.util.List;
33  import java.util.Map;
34  
35  import org.apache.hc.core5.http.Header;
36  import org.apache.hc.core5.util.Asserts;
37  
38  final class OutboundDynamicTable {
39  
40      private final StaticTable staticTable;
41      private final FifoLinkedList headers;
42      private final Map<String, LinkedList<HPackEntry>> mapByName;
43  
44      private int maxSize;
45      private int currentSize;
46  
47      OutboundDynamicTable(final StaticTable staticTable) {
48          this.staticTable = staticTable;
49          this.headers = new FifoLinkedList();
50          this.mapByName = new HashMap<>();
51          this.maxSize = Integer.MAX_VALUE;
52          this.currentSize = 0;
53      }
54  
55      OutboundDynamicTable() {
56          this(StaticTable.INSTANCE);
57      }
58  
59      public int getMaxSize() {
60          return maxSize;
61      }
62  
63      public void setMaxSize(final int maxSize) {
64          this.maxSize = maxSize;
65          evict();
66      }
67  
68      public int getCurrentSize() {
69          return currentSize;
70      }
71  
72      int staticLength() {
73          return staticTable.length();
74      }
75  
76      int dynamicLength() {
77          return headers.size();
78      }
79  
80      Header getDynamicEntry(final int index) {
81          return headers.get(index);
82      }
83  
84      public int length() {
85          return staticTable.length() + headers.size();
86      }
87  
88      public Header getHeader(final int index) {
89          if (index < 1 || index > length()) {
90              throw new IndexOutOfBoundsException();
91          }
92          return index <= staticTable.length()
93                          ? staticTable.get(index)
94                          : headers.get(index - staticTable.length() - 1);
95      }
96  
97      public void add(final HPackHeader header) {
98          final int entrySize = header.getTotalSize();
99          if (entrySize > this.maxSize) {
100             clear();
101             this.mapByName.clear();
102             return;
103         }
104         final String key = header.getName();
105         final FifoLinkedList.InternalNode node = headers.addFirst(header);
106         LinkedList<HPackEntry> nodes = mapByName.get(key);
107         if (nodes == null) {
108             nodes = new LinkedList<>();
109             mapByName.put(key, nodes);
110         }
111         nodes.addFirst(node);
112         currentSize += entrySize;
113         evict();
114     }
115 
116     private void clear() {
117         currentSize = 0;
118         headers.clear();
119     }
120 
121     public List<HPackEntry> getByName(final String key) {
122         return this.mapByName.get(key);
123     }
124 
125     private void evict() {
126         while (currentSize > maxSize) {
127             final FifoLinkedList.InternalNode node = headers.removeLast();
128             if (node != null) {
129                 final HPackHeader header = node.getHeader();
130                 currentSize -= header.getTotalSize();
131 
132                 final String key = header.getName();
133                 final LinkedList<HPackEntry> nodes = mapByName.get(key);
134                 if (nodes != null) {
135                     nodes.remove(node);
136                 }
137             } else {
138                 Asserts.check(currentSize == 0, "Current table size must be zero");
139                 break;
140             }
141         }
142     }
143 
144 }