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.Arrays;
26 import java.util.List;
27 import java.util.Locale;
28
29 import org.eclipse.jetty.spdy.CompressionDictionary;
30 import org.eclipse.jetty.spdy.CompressionFactory;
31 import org.eclipse.jetty.spdy.api.SPDY;
32 import org.eclipse.jetty.util.Fields;
33
34 public class HeadersBlockGenerator
35 {
36 private final CompressionFactory.Compressor compressor;
37 private boolean needsDictionary = true;
38
39 public HeadersBlockGenerator(CompressionFactory.Compressor compressor)
40 {
41 this.compressor = compressor;
42 }
43
44 public ByteBuffer generate(short version, Fields headers)
45 {
46
47 final Charset iso1 = StandardCharsets.ISO_8859_1;
48 ByteArrayOutputStream buffer = new ByteArrayOutputStream(headers.getSize() * 64);
49 writeCount(version, buffer, headers.getSize());
50 for (Fields.Field header : headers)
51 {
52 String name = header.getName().toLowerCase(Locale.ENGLISH);
53 byte[] nameBytes = name.getBytes(iso1);
54 writeNameLength(version, buffer, nameBytes.length);
55 buffer.write(nameBytes, 0, nameBytes.length);
56
57
58 String value = header.getValue();
59 byte[] valueBytes = value.getBytes(iso1);
60 if (header.hasMultipleValues())
61 {
62 List<String> values = header.getValues();
63 for (int i = 1; i < values.size(); ++i)
64 {
65 byte[] moreValueBytes = values.get(i).getBytes(iso1);
66 byte[] newValueBytes = Arrays.copyOf(valueBytes,valueBytes.length + 1 + moreValueBytes.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 }