1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.spdy;
17
18 import io.netty.channel.ChannelHandlerContext;
19 import io.netty.handler.codec.MessageToMessageEncoder;
20 import io.netty.handler.codec.UnsupportedMessageTypeException;
21 import io.netty.handler.codec.http.FullHttpMessage;
22 import io.netty.handler.codec.http.HttpContent;
23 import io.netty.handler.codec.http.HttpHeaderNames;
24 import io.netty.handler.codec.http.HttpHeaders;
25 import io.netty.handler.codec.http.HttpMessage;
26 import io.netty.handler.codec.http.HttpObject;
27 import io.netty.handler.codec.http.HttpRequest;
28 import io.netty.handler.codec.http.HttpResponse;
29 import io.netty.handler.codec.http.LastHttpContent;
30 import io.netty.util.AsciiString;
31 import io.netty.util.internal.ObjectUtil;
32
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Map.Entry;
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123 public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
124
125 private int currentStreamId;
126
127 private final boolean validateHeaders;
128 private final boolean headersToLowerCase;
129
130
131
132
133
134
135 public SpdyHttpEncoder(SpdyVersion version) {
136 this(version, true, true);
137 }
138
139
140
141
142
143
144
145
146
147 public SpdyHttpEncoder(SpdyVersion version, boolean headersToLowerCase, boolean validateHeaders) {
148 super(HttpObject.class);
149 ObjectUtil.checkNotNull(version, "version");
150 this.headersToLowerCase = headersToLowerCase;
151 this.validateHeaders = validateHeaders;
152 }
153
154 @Override
155 protected void encode(ChannelHandlerContext ctx, HttpObject msg, List<Object> out) throws Exception {
156
157 boolean valid = false;
158 boolean last = false;
159
160 if (msg instanceof HttpRequest) {
161
162 HttpRequest httpRequest = (HttpRequest) msg;
163 SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpRequest);
164 out.add(spdySynStreamFrame);
165
166 last = spdySynStreamFrame.isLast() || spdySynStreamFrame.isUnidirectional();
167 valid = true;
168 }
169 if (msg instanceof HttpResponse) {
170
171 HttpResponse httpResponse = (HttpResponse) msg;
172 SpdyHeadersFrame spdyHeadersFrame = createHeadersFrame(httpResponse);
173 out.add(spdyHeadersFrame);
174
175 last = spdyHeadersFrame.isLast();
176 valid = true;
177 }
178 if (msg instanceof HttpContent && !last) {
179
180 HttpContent chunk = (HttpContent) msg;
181
182 chunk.content().retain();
183 SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamId, chunk.content());
184 if (chunk instanceof LastHttpContent) {
185 LastHttpContent trailer = (LastHttpContent) chunk;
186 HttpHeaders trailers = trailer.trailingHeaders();
187 if (trailers.isEmpty()) {
188 spdyDataFrame.setLast(true);
189 out.add(spdyDataFrame);
190 } else {
191
192 SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(currentStreamId, validateHeaders);
193 spdyHeadersFrame.setLast(true);
194 Iterator<Entry<CharSequence, CharSequence>> itr = trailers.iteratorCharSequence();
195 while (itr.hasNext()) {
196 Map.Entry<CharSequence, CharSequence> entry = itr.next();
197 final CharSequence headerName =
198 headersToLowerCase ? AsciiString.of(entry.getKey()).toLowerCase() : entry.getKey();
199 spdyHeadersFrame.headers().add(headerName, entry.getValue());
200 }
201
202
203 out.add(spdyDataFrame);
204 out.add(spdyHeadersFrame);
205 }
206 } else {
207 out.add(spdyDataFrame);
208 }
209
210 valid = true;
211 }
212
213 if (!valid) {
214 throw new UnsupportedMessageTypeException(msg);
215 }
216 }
217
218 @SuppressWarnings("deprecation")
219 private SpdySynStreamFrame createSynStreamFrame(HttpRequest httpRequest) throws Exception {
220
221 final HttpHeaders httpHeaders = httpRequest.headers();
222 int streamId = httpHeaders.getInt(SpdyHttpHeaders.Names.STREAM_ID);
223 int associatedToStreamId = httpHeaders.getInt(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID, 0);
224 byte priority = (byte) httpHeaders.getInt(SpdyHttpHeaders.Names.PRIORITY, 0);
225 CharSequence scheme = httpHeaders.get(SpdyHttpHeaders.Names.SCHEME);
226 httpHeaders.remove(SpdyHttpHeaders.Names.STREAM_ID);
227 httpHeaders.remove(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID);
228 httpHeaders.remove(SpdyHttpHeaders.Names.PRIORITY);
229 httpHeaders.remove(SpdyHttpHeaders.Names.SCHEME);
230
231
232
233 httpHeaders.remove(HttpHeaderNames.CONNECTION);
234 httpHeaders.remove("Keep-Alive");
235 httpHeaders.remove("Proxy-Connection");
236 httpHeaders.remove(HttpHeaderNames.TRANSFER_ENCODING);
237
238 SpdySynStreamFrame spdySynStreamFrame =
239 new DefaultSpdySynStreamFrame(streamId, associatedToStreamId, priority, validateHeaders);
240
241
242 SpdyHeaders frameHeaders = spdySynStreamFrame.headers();
243 frameHeaders.set(SpdyHeaders.HttpNames.METHOD, httpRequest.method().name());
244 frameHeaders.set(SpdyHeaders.HttpNames.PATH, httpRequest.uri());
245 frameHeaders.set(SpdyHeaders.HttpNames.VERSION, httpRequest.protocolVersion().text());
246
247
248 CharSequence host = httpHeaders.get(HttpHeaderNames.HOST);
249 httpHeaders.remove(HttpHeaderNames.HOST);
250 frameHeaders.set(SpdyHeaders.HttpNames.HOST, host);
251
252
253 if (scheme == null) {
254 scheme = "https";
255 }
256 frameHeaders.set(SpdyHeaders.HttpNames.SCHEME, scheme);
257
258
259 Iterator<Entry<CharSequence, CharSequence>> itr = httpHeaders.iteratorCharSequence();
260 while (itr.hasNext()) {
261 Map.Entry<CharSequence, CharSequence> entry = itr.next();
262 final CharSequence headerName =
263 headersToLowerCase ? AsciiString.of(entry.getKey()).toLowerCase() : entry.getKey();
264 frameHeaders.add(headerName, entry.getValue());
265 }
266 currentStreamId = spdySynStreamFrame.streamId();
267 if (associatedToStreamId == 0) {
268 spdySynStreamFrame.setLast(isLast(httpRequest));
269 } else {
270 spdySynStreamFrame.setUnidirectional(true);
271 }
272
273 return spdySynStreamFrame;
274 }
275
276 @SuppressWarnings("deprecation")
277 private SpdyHeadersFrame createHeadersFrame(HttpResponse httpResponse) throws Exception {
278
279 final HttpHeaders httpHeaders = httpResponse.headers();
280 int streamId = httpHeaders.getInt(SpdyHttpHeaders.Names.STREAM_ID);
281 httpHeaders.remove(SpdyHttpHeaders.Names.STREAM_ID);
282
283
284
285 httpHeaders.remove(HttpHeaderNames.CONNECTION);
286 httpHeaders.remove("Keep-Alive");
287 httpHeaders.remove("Proxy-Connection");
288 httpHeaders.remove(HttpHeaderNames.TRANSFER_ENCODING);
289
290 SpdyHeadersFrame spdyHeadersFrame;
291 if (SpdyCodecUtil.isServerId(streamId)) {
292 spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamId, validateHeaders);
293 } else {
294 spdyHeadersFrame = new DefaultSpdySynReplyFrame(streamId, validateHeaders);
295 }
296 SpdyHeaders frameHeaders = spdyHeadersFrame.headers();
297
298 frameHeaders.set(SpdyHeaders.HttpNames.STATUS, httpResponse.status().codeAsText());
299 frameHeaders.set(SpdyHeaders.HttpNames.VERSION, httpResponse.protocolVersion().text());
300
301
302 Iterator<Entry<CharSequence, CharSequence>> itr = httpHeaders.iteratorCharSequence();
303 while (itr.hasNext()) {
304 Map.Entry<CharSequence, CharSequence> entry = itr.next();
305 final CharSequence headerName =
306 headersToLowerCase ? AsciiString.of(entry.getKey()).toLowerCase() : entry.getKey();
307 spdyHeadersFrame.headers().add(headerName, entry.getValue());
308 }
309
310 currentStreamId = streamId;
311 spdyHeadersFrame.setLast(isLast(httpResponse));
312
313 return spdyHeadersFrame;
314 }
315
316
317
318
319
320
321
322 private static boolean isLast(HttpMessage httpMessage) {
323 if (httpMessage instanceof FullHttpMessage) {
324 FullHttpMessage fullMessage = (FullHttpMessage) httpMessage;
325 if (fullMessage.trailingHeaders().isEmpty() && !fullMessage.content().isReadable()) {
326 return true;
327 }
328 }
329
330 return false;
331 }
332 }