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