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.HttpHeaders;
24 import io.netty.handler.codec.http.HttpMessage;
25 import io.netty.handler.codec.http.HttpObject;
26 import io.netty.handler.codec.http.HttpRequest;
27 import io.netty.handler.codec.http.HttpResponse;
28 import io.netty.handler.codec.http.LastHttpContent;
29
30 import java.util.List;
31 import java.util.Map;
32
33
34
35
36
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 public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
119
120 private int currentStreamId;
121
122
123
124
125
126
127 public SpdyHttpEncoder(SpdyVersion version) {
128 if (version == null) {
129 throw new NullPointerException("version");
130 }
131 }
132
133 @Override
134 protected void encode(ChannelHandlerContext ctx, HttpObject msg, List<Object> out) throws Exception {
135
136 boolean valid = false;
137 boolean last = false;
138
139 if (msg instanceof HttpRequest) {
140
141 HttpRequest httpRequest = (HttpRequest) msg;
142 SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpRequest);
143 out.add(spdySynStreamFrame);
144
145 last = spdySynStreamFrame.isLast() || spdySynStreamFrame.isUnidirectional();
146 valid = true;
147 }
148 if (msg instanceof HttpResponse) {
149
150 HttpResponse httpResponse = (HttpResponse) msg;
151 SpdyHeadersFrame spdyHeadersFrame = createHeadersFrame(httpResponse);
152 out.add(spdyHeadersFrame);
153
154 last = spdyHeadersFrame.isLast();
155 valid = true;
156 }
157 if (msg instanceof HttpContent && !last) {
158
159 HttpContent chunk = (HttpContent) msg;
160
161 chunk.content().retain();
162 SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamId, chunk.content());
163 if (chunk instanceof LastHttpContent) {
164 LastHttpContent trailer = (LastHttpContent) chunk;
165 HttpHeaders trailers = trailer.trailingHeaders();
166 if (trailers.isEmpty()) {
167 spdyDataFrame.setLast(true);
168 out.add(spdyDataFrame);
169 } else {
170
171 SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(currentStreamId);
172 spdyHeadersFrame.setLast(true);
173 for (Map.Entry<String, String> entry: trailers) {
174 spdyHeadersFrame.headers().add(entry.getKey(), entry.getValue());
175 }
176
177
178 out.add(spdyDataFrame);
179 out.add(spdyHeadersFrame);
180 }
181 } else {
182 out.add(spdyDataFrame);
183 }
184
185 valid = true;
186 }
187
188 if (!valid) {
189 throw new UnsupportedMessageTypeException(msg);
190 }
191 }
192
193 @SuppressWarnings("deprecation")
194 private SpdySynStreamFrame createSynStreamFrame(HttpRequest httpRequest) throws Exception {
195
196 final HttpHeaders httpHeaders = httpRequest.headers();
197 int streamId = SpdyHttpHeaders.getStreamId(httpRequest);
198 int associatedToStreamId = SpdyHttpHeaders.getAssociatedToStreamId(httpRequest);
199 byte priority = SpdyHttpHeaders.getPriority(httpRequest);
200 CharSequence scheme = httpHeaders.get(SpdyHttpHeaders.Names.SCHEME);
201 httpHeaders.remove(SpdyHttpHeaders.Names.STREAM_ID);
202 httpHeaders.remove(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID);
203 httpHeaders.remove(SpdyHttpHeaders.Names.PRIORITY);
204 httpHeaders.remove(SpdyHttpHeaders.Names.SCHEME);
205
206
207
208 httpHeaders.remove(HttpHeaders.Names.CONNECTION);
209 httpHeaders.remove("Keep-Alive");
210 httpHeaders.remove("Proxy-Connection");
211 httpHeaders.remove(HttpHeaders.Names.TRANSFER_ENCODING);
212
213 SpdySynStreamFrame spdySynStreamFrame =
214 new DefaultSpdySynStreamFrame(streamId, associatedToStreamId, priority);
215
216
217 SpdyHeaders frameHeaders = spdySynStreamFrame.headers();
218 frameHeaders.set(SpdyHeaders.HttpNames.METHOD, httpRequest.getMethod());
219 frameHeaders.set(SpdyHeaders.HttpNames.PATH, httpRequest.getUri());
220 frameHeaders.set(SpdyHeaders.HttpNames.VERSION, httpRequest.getProtocolVersion());
221
222
223 CharSequence host = httpHeaders.get(HttpHeaders.Names.HOST);
224 httpHeaders.remove(HttpHeaders.Names.HOST);
225 frameHeaders.set(SpdyHeaders.HttpNames.HOST, host);
226
227
228 if (scheme == null) {
229 scheme = "https";
230 }
231 frameHeaders.set(SpdyHeaders.HttpNames.SCHEME, scheme);
232
233
234 for (Map.Entry<String, String> entry: httpHeaders) {
235 frameHeaders.add(entry.getKey(), entry.getValue());
236 }
237 currentStreamId = spdySynStreamFrame.streamId();
238 if (associatedToStreamId == 0) {
239 spdySynStreamFrame.setLast(isLast(httpRequest));
240 } else {
241 spdySynStreamFrame.setUnidirectional(true);
242 }
243
244 return spdySynStreamFrame;
245 }
246
247 @SuppressWarnings("deprecation")
248 private SpdyHeadersFrame createHeadersFrame(HttpResponse httpResponse) throws Exception {
249
250 final HttpHeaders httpHeaders = httpResponse.headers();
251 int streamId = SpdyHttpHeaders.getStreamId(httpResponse);
252 httpHeaders.remove(SpdyHttpHeaders.Names.STREAM_ID);
253
254
255
256 httpHeaders.remove(HttpHeaders.Names.CONNECTION);
257 httpHeaders.remove("Keep-Alive");
258 httpHeaders.remove("Proxy-Connection");
259 httpHeaders.remove(HttpHeaders.Names.TRANSFER_ENCODING);
260
261 SpdyHeadersFrame spdyHeadersFrame;
262 if (SpdyCodecUtil.isServerId(streamId)) {
263 spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamId);
264 } else {
265 spdyHeadersFrame = new DefaultSpdySynReplyFrame(streamId);
266 }
267 SpdyHeaders frameHeaders = spdyHeadersFrame.headers();
268
269 frameHeaders.set(SpdyHeaders.HttpNames.STATUS, httpResponse.getStatus().code());
270 frameHeaders.set(SpdyHeaders.HttpNames.VERSION, httpResponse.getProtocolVersion());
271
272
273 for (Map.Entry<String, String> entry: httpHeaders) {
274 spdyHeadersFrame.headers().add(entry.getKey(), entry.getValue());
275 }
276
277 currentStreamId = streamId;
278 spdyHeadersFrame.setLast(isLast(httpResponse));
279
280 return spdyHeadersFrame;
281 }
282
283
284
285
286
287
288
289 private static boolean isLast(HttpMessage httpMessage) {
290 if (httpMessage instanceof FullHttpMessage) {
291 FullHttpMessage fullMessage = (FullHttpMessage) httpMessage;
292 if (fullMessage.trailingHeaders().isEmpty() && !fullMessage.content().isReadable()) {
293 return true;
294 }
295 }
296
297 return false;
298 }
299 }