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.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 Charset iso1 = Charset.forName("ISO-8859-1");
45 ByteArrayOutputStream buffer = new ByteArrayOutputStream(headers.size() * 64);
46 writeCount(version, buffer, headers.size());
47 for (Fields.Field header : headers)
48 {
49 String name = header.name().toLowerCase(Locale.ENGLISH);
50 byte[] nameBytes = name.getBytes(iso1);
51 writeNameLength(version, buffer, nameBytes.length);
52 buffer.write(nameBytes, 0, nameBytes.length);
53
54
55 String value = header.value();
56 byte[] valueBytes = value.getBytes(iso1);
57 if (header.hasMultipleValues())
58 {
59 String[] values = header.values();
60 for (int i = 1; i < values.length; ++i)
61 {
62 byte[] moreValueBytes = values[i].getBytes(iso1);
63 byte[] newValueBytes = new byte[valueBytes.length + 1 + moreValueBytes.length];
64 System.arraycopy(valueBytes, 0, newValueBytes, 0, valueBytes.length);
65 newValueBytes[valueBytes.length] = 0;
66 System.arraycopy(moreValueBytes, 0, newValueBytes, valueBytes.length + 1, moreValueBytes.length);
67 valueBytes = newValueBytes;
68 }
69 }
70
71 writeValueLength(version, buffer, valueBytes.length);
72 buffer.write(valueBytes, 0, valueBytes.length);
73 }
74
75 return compress(version, buffer.toByteArray());
76 }
77
78 private ByteBuffer compress(short version, byte[] bytes)
79 {
80 ByteArrayOutputStream buffer = new ByteArrayOutputStream(bytes.length);
81
82
83 synchronized (compressor)
84 {
85 if (needsDictionary)
86 {
87 compressor.setDictionary(CompressionDictionary.get(version));
88 needsDictionary = false;
89 }
90
91 compressor.setInput(bytes);
92
93
94
95
96 buffer.reset();
97 int compressed;
98 byte[] output = new byte[Math.max(256, bytes.length)];
99 while (true)
100 {
101
102 compressed = compressor.compress(output);
103 buffer.write(output, 0, compressed);
104 if (compressed < output.length)
105 break;
106 }
107 }
108
109 return ByteBuffer.wrap(buffer.toByteArray());
110 }
111
112 private void writeCount(short version, ByteArrayOutputStream buffer, int value)
113 {
114 switch (version)
115 {
116 case SPDY.V2:
117 {
118 buffer.write((value & 0xFF_00) >>> 8);
119 buffer.write(value & 0x00_FF);
120 break;
121 }
122 case SPDY.V3:
123 {
124 buffer.write((value & 0xFF_00_00_00) >>> 24);
125 buffer.write((value & 0x00_FF_00_00) >>> 16);
126 buffer.write((value & 0x00_00_FF_00) >>> 8);
127 buffer.write(value & 0x00_00_00_FF);
128 break;
129 }
130 default:
131 {
132
133
134 throw new IllegalStateException();
135 }
136 }
137 }
138
139 private void writeNameLength(short version, ByteArrayOutputStream buffer, int length)
140 {
141 writeCount(version, buffer, length);
142 }
143
144 private void writeValueLength(short version, ByteArrayOutputStream buffer, int length)
145 {
146 writeCount(version, buffer, length);
147 }
148 }