1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 }