1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.spdy.generator;
20
21 import java.io.ByteArrayOutputStream;
22 import java.nio.ByteBuffer;
23 import java.nio.charset.StandardCharsets;
24 import java.util.Locale;
25
26 import org.eclipse.jetty.spdy.CompressionDictionary;
27 import org.eclipse.jetty.spdy.CompressionFactory;
28 import org.eclipse.jetty.spdy.api.SPDY;
29 import org.eclipse.jetty.util.Fields;
30
31 public class HeadersBlockGenerator
32 {
33 private final CompressionFactory.Compressor compressor;
34 private boolean needsDictionary = true;
35
36 public HeadersBlockGenerator(CompressionFactory.Compressor compressor)
37 {
38 this.compressor = compressor;
39 }
40
41 public ByteBuffer generate(short version, Fields headers)
42 {
43
44 ByteArrayOutputStream buffer = new ByteArrayOutputStream(headers.size() * 64);
45 writeCount(version, buffer, headers.size());
46 for (Fields.Field header : headers)
47 {
48 String name = header.name().toLowerCase(Locale.ENGLISH);
49 byte[] nameBytes = name.getBytes(StandardCharsets.ISO_8859_1);
50 writeNameLength(version, buffer, nameBytes.length);
51 buffer.write(nameBytes, 0, nameBytes.length);
52
53
54 String value = header.value();
55 byte[] valueBytes = value.getBytes(StandardCharsets.ISO_8859_1);
56 if (header.hasMultipleValues())
57 {
58 String[] values = header.values();
59 for (int i = 1; i < values.length; ++i)
60 {
61 byte[] moreValueBytes = values[i].getBytes(StandardCharsets.ISO_8859_1);
62 byte[] newValueBytes = new byte[valueBytes.length + 1 + moreValueBytes.length];
63 System.arraycopy(valueBytes, 0, newValueBytes, 0, valueBytes.length);
64 newValueBytes[valueBytes.length] = 0;
65 System.arraycopy(moreValueBytes, 0, newValueBytes, valueBytes.length + 1, moreValueBytes.length);
66 valueBytes = newValueBytes;
67 }
68 }
69
70 writeValueLength(version, buffer, valueBytes.length);
71 buffer.write(valueBytes, 0, valueBytes.length);
72 }
73
74 return compress(version, buffer.toByteArray());
75 }
76
77 private ByteBuffer compress(short version, byte[] bytes)
78 {
79 ByteArrayOutputStream buffer = new ByteArrayOutputStream(bytes.length);
80
81
82 synchronized (compressor)
83 {
84 if (needsDictionary)
85 {
86 compressor.setDictionary(CompressionDictionary.get(version));
87 needsDictionary = false;
88 }
89
90 compressor.setInput(bytes);
91
92
93
94
95 buffer.reset();
96 int compressed;
97 byte[] output = new byte[Math.max(256, bytes.length)];
98 while (true)
99 {
100
101 compressed = compressor.compress(output);
102 buffer.write(output, 0, compressed);
103 if (compressed < output.length)
104 break;
105 }
106 }
107
108 return ByteBuffer.wrap(buffer.toByteArray());
109 }
110
111 private void writeCount(short version, ByteArrayOutputStream buffer, int value)
112 {
113 switch (version)
114 {
115 case SPDY.V2:
116 {
117 buffer.write((value & 0xFF_00) >>> 8);
118 buffer.write(value & 0x00_FF);
119 break;
120 }
121 case SPDY.V3:
122 {
123 buffer.write((value & 0xFF_00_00_00) >>> 24);
124 buffer.write((value & 0x00_FF_00_00) >>> 16);
125 buffer.write((value & 0x00_00_FF_00) >>> 8);
126 buffer.write(value & 0x00_00_00_FF);
127 break;
128 }
129 default:
130 {
131
132
133 throw new IllegalStateException();
134 }
135 }
136 }
137
138 private void writeNameLength(short version, ByteArrayOutputStream buffer, int length)
139 {
140 writeCount(version, buffer, length);
141 }
142
143 private void writeValueLength(short version, ByteArrayOutputStream buffer, int length)
144 {
145 writeCount(version, buffer, length);
146 }
147 }