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.Args;
37 import org.apache.hc.core5.util.Asserts;
38
39 final class OutboundDynamicTable {
40
41 private final StaticTable staticTable;
42 private final FifoLinkedList headers;
43 private final Map<String, LinkedList<HPackEntry>> mapByName;
44
45 private int maxSize;
46 private int currentSize;
47
48 OutboundDynamicTable(final StaticTable staticTable) {
49 this.staticTable = staticTable;
50 this.headers = new FifoLinkedList();
51 this.mapByName = new HashMap<>();
52 this.maxSize = Integer.MAX_VALUE;
53 this.currentSize = 0;
54 }
55
56 OutboundDynamicTable() {
57 this(StaticTable.INSTANCE);
58 }
59
60 public int getMaxSize() {
61 return maxSize;
62 }
63
64 public void setMaxSize(final int maxSize) {
65 this.maxSize = maxSize;
66 evict();
67 }
68
69 public int getCurrentSize() {
70 return currentSize;
71 }
72
73 int staticLength() {
74 return staticTable.length();
75 }
76
77 int dynamicLength() {
78 return headers.size();
79 }
80
81 Header getDynamicEntry(final int index) {
82 return headers.get(index);
83 }
84
85 public int length() {
86 return staticTable.length() + headers.size();
87 }
88
89 public Header getHeader(final int index) {
90 final int length = length();
91 Args.check(index >= 1, "index %s cannot be less then 1", index);
92 Args.check(index <= length, "index %s cannot be greater then length %s ", index, length);
93 return index <= staticTable.length()
94 ? staticTable.get(index)
95 : headers.get(index - staticTable.length() - 1);
96 }
97
98 public void add(final HPackHeader header) {
99 final int entrySize = header.getTotalSize();
100 if (entrySize > this.maxSize) {
101 clear();
102 this.mapByName.clear();
103 return;
104 }
105 final String key = header.getName();
106 final FifoLinkedList.InternalNode node = headers.addFirst(header);
107 final LinkedList<HPackEntry> nodes = mapByName.computeIfAbsent(key, k -> new LinkedList<>());
108 nodes.addFirst(node);
109 currentSize += entrySize;
110 evict();
111 }
112
113 private void clear() {
114 currentSize = 0;
115 headers.clear();
116 }
117
118 public List<HPackEntry> getByName(final String key) {
119 return this.mapByName.get(key);
120 }
121
122 private void evict() {
123 while (currentSize > maxSize) {
124 final FifoLinkedList.InternalNode node = headers.removeLast();
125 if (node != null) {
126 final HPackHeader header = node.getHeader();
127 currentSize -= header.getTotalSize();
128
129 final String key = header.getName();
130 final LinkedList<HPackEntry> nodes = mapByName.get(key);
131 if (nodes != null) {
132 nodes.remove(node);
133 }
134 } else {
135 Asserts.check(currentSize == 0, "Current table size must be zero");
136 break;
137 }
138 }
139 }
140
141 }