• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.squareup.okhttp.internal.framed;
17 
18 import com.squareup.okhttp.Protocol;
19 import com.squareup.okhttp.internal.Util;
20 import java.io.IOException;
21 import java.io.UnsupportedEncodingException;
22 import java.net.ProtocolException;
23 import java.util.List;
24 import java.util.zip.Deflater;
25 import okio.Buffer;
26 import okio.BufferedSink;
27 import okio.BufferedSource;
28 import okio.ByteString;
29 import okio.DeflaterSink;
30 import okio.Okio;
31 
32 /**
33  * Read and write spdy/3.1 frames.
34  * http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1
35  */
36 public final class Spdy3 implements Variant {
37 
getProtocol()38   @Override public Protocol getProtocol() {
39     return Protocol.SPDY_3;
40   }
41 
42   static final int TYPE_DATA = 0x0;
43   static final int TYPE_SYN_STREAM = 0x1;
44   static final int TYPE_SYN_REPLY = 0x2;
45   static final int TYPE_RST_STREAM = 0x3;
46   static final int TYPE_SETTINGS = 0x4;
47   static final int TYPE_PING = 0x6;
48   static final int TYPE_GOAWAY = 0x7;
49   static final int TYPE_HEADERS = 0x8;
50   static final int TYPE_WINDOW_UPDATE = 0x9;
51 
52   static final int FLAG_FIN = 0x1;
53   static final int FLAG_UNIDIRECTIONAL = 0x2;
54 
55   static final int VERSION = 3;
56 
57   static final byte[] DICTIONARY;
58   static {
59     try {
60       DICTIONARY = ("\u0000\u0000\u0000\u0007options\u0000\u0000\u0000\u0004hea"
61           + "d\u0000\u0000\u0000\u0004post\u0000\u0000\u0000\u0003put\u0000\u0000\u0000\u0006dele"
62           + "te\u0000\u0000\u0000\u0005trace\u0000\u0000\u0000\u0006accept\u0000\u0000\u0000"
63           + "\u000Eaccept-charset\u0000\u0000\u0000\u000Faccept-encoding\u0000\u0000\u0000\u000Fa"
64           + "ccept-language\u0000\u0000\u0000\raccept-ranges\u0000\u0000\u0000\u0003age\u0000"
65           + "\u0000\u0000\u0005allow\u0000\u0000\u0000\rauthorization\u0000\u0000\u0000\rcache-co"
66           + "ntrol\u0000\u0000\u0000\nconnection\u0000\u0000\u0000\fcontent-base\u0000\u0000"
67           + "\u0000\u0010content-encoding\u0000\u0000\u0000\u0010content-language\u0000\u0000"
68           + "\u0000\u000Econtent-length\u0000\u0000\u0000\u0010content-location\u0000\u0000\u0000"
69           + "\u000Bcontent-md5\u0000\u0000\u0000\rcontent-range\u0000\u0000\u0000\fcontent-type"
70           + "\u0000\u0000\u0000\u0004date\u0000\u0000\u0000\u0004etag\u0000\u0000\u0000\u0006expe"
71           + "ct\u0000\u0000\u0000\u0007expires\u0000\u0000\u0000\u0004from\u0000\u0000\u0000"
72           + "\u0004host\u0000\u0000\u0000\bif-match\u0000\u0000\u0000\u0011if-modified-since"
73           + "\u0000\u0000\u0000\rif-none-match\u0000\u0000\u0000\bif-range\u0000\u0000\u0000"
74           + "\u0013if-unmodified-since\u0000\u0000\u0000\rlast-modified\u0000\u0000\u0000\blocati"
75           + "on\u0000\u0000\u0000\fmax-forwards\u0000\u0000\u0000\u0006pragma\u0000\u0000\u0000"
76           + "\u0012proxy-authenticate\u0000\u0000\u0000\u0013proxy-authorization\u0000\u0000"
77           + "\u0000\u0005range\u0000\u0000\u0000\u0007referer\u0000\u0000\u0000\u000Bretry-after"
78           + "\u0000\u0000\u0000\u0006server\u0000\u0000\u0000\u0002te\u0000\u0000\u0000\u0007trai"
79           + "ler\u0000\u0000\u0000\u0011transfer-encoding\u0000\u0000\u0000\u0007upgrade\u0000"
80           + "\u0000\u0000\nuser-agent\u0000\u0000\u0000\u0004vary\u0000\u0000\u0000\u0003via"
81           + "\u0000\u0000\u0000\u0007warning\u0000\u0000\u0000\u0010www-authenticate\u0000\u0000"
82           + "\u0000\u0006method\u0000\u0000\u0000\u0003get\u0000\u0000\u0000\u0006status\u0000"
83           + "\u0000\u0000\u0006200 OK\u0000\u0000\u0000\u0007version\u0000\u0000\u0000\bHTTP/1.1"
84           + "\u0000\u0000\u0000\u0003url\u0000\u0000\u0000\u0006public\u0000\u0000\u0000\nset-coo"
85           + "kie\u0000\u0000\u0000\nkeep-alive\u0000\u0000\u0000\u0006origin100101201202205206300"
86           + "302303304305306307402405406407408409410411412413414415416417502504505203 Non-Authori"
87           + "tative Information204 No Content301 Moved Permanently400 Bad Request401 Unauthorized"
88           + "403 Forbidden404 Not Found500 Internal Server Error501 Not Implemented503 Service Un"
89           + "availableJan Feb Mar Apr May Jun Jul Aug Sept Oct Nov Dec 00:00:00 Mon, Tue, Wed, Th"
90           + "u, Fri, Sat, Sun, GMTchunked,text/html,image/png,image/jpg,image/gif,application/xml"
91           + ",application/xhtml+xml,text/plain,text/javascript,publicprivatemax-age=gzip,deflate,"
92           + "sdchcharset=utf-8charset=iso-8859-1,utf-,*,enq=0.").getBytes(Util.UTF_8.name());
93     } catch (UnsupportedEncodingException e) {
94       throw new AssertionError();
95     }
96   }
97 
newReader(BufferedSource source, boolean client)98   @Override public FrameReader newReader(BufferedSource source, boolean client) {
99     return new Reader(source, client);
100   }
101 
newWriter(BufferedSink sink, boolean client)102   @Override public FrameWriter newWriter(BufferedSink sink, boolean client) {
103     return new Writer(sink, client);
104   }
105 
106   /** Read spdy/3 frames. */
107   static final class Reader implements FrameReader {
108     private final BufferedSource source;
109     private final boolean client;
110     private final NameValueBlockReader headerBlockReader;
111 
Reader(BufferedSource source, boolean client)112     Reader(BufferedSource source, boolean client) {
113       this.source = source;
114       this.headerBlockReader = new NameValueBlockReader(this.source);
115       this.client = client;
116     }
117 
readConnectionPreface()118     @Override public void readConnectionPreface() {
119     }
120 
121     /**
122      * Send the next frame to {@code handler}. Returns true unless there are no
123      * more frames on the stream.
124      */
nextFrame(Handler handler)125     @Override public boolean nextFrame(Handler handler) throws IOException {
126       int w1;
127       int w2;
128       try {
129         w1 = source.readInt();
130         w2 = source.readInt();
131       } catch (IOException e) {
132         return false; // This might be a normal socket close.
133       }
134 
135       boolean control = (w1 & 0x80000000) != 0;
136       int flags = (w2 & 0xff000000) >>> 24;
137       int length = (w2 & 0xffffff);
138 
139       if (control) {
140         int version = (w1 & 0x7fff0000) >>> 16;
141         int type = (w1 & 0xffff);
142 
143         if (version != 3) {
144           throw new ProtocolException("version != 3: " + version);
145         }
146 
147         switch (type) {
148           case TYPE_SYN_STREAM:
149             readSynStream(handler, flags, length);
150             return true;
151 
152           case TYPE_SYN_REPLY:
153             readSynReply(handler, flags, length);
154             return true;
155 
156           case TYPE_RST_STREAM:
157             readRstStream(handler, flags, length);
158             return true;
159 
160           case TYPE_SETTINGS:
161             readSettings(handler, flags, length);
162             return true;
163 
164           case TYPE_PING:
165             readPing(handler, flags, length);
166             return true;
167 
168           case TYPE_GOAWAY:
169             readGoAway(handler, flags, length);
170             return true;
171 
172           case TYPE_HEADERS:
173             readHeaders(handler, flags, length);
174             return true;
175 
176           case TYPE_WINDOW_UPDATE:
177             readWindowUpdate(handler, flags, length);
178             return true;
179 
180           default:
181             source.skip(length);
182             return true;
183         }
184       } else {
185         int streamId = w1 & 0x7fffffff;
186         boolean inFinished = (flags & FLAG_FIN) != 0;
187         handler.data(inFinished, streamId, source, length);
188         return true;
189       }
190     }
191 
readSynStream(Handler handler, int flags, int length)192     private void readSynStream(Handler handler, int flags, int length) throws IOException {
193       int w1 = source.readInt();
194       int w2 = source.readInt();
195       int streamId = w1 & 0x7fffffff;
196       int associatedStreamId = w2 & 0x7fffffff;
197       source.readShort(); // int priority = (s3 & 0xe000) >>> 13; int slot = s3 & 0xff;
198       List<Header> headerBlock = headerBlockReader.readNameValueBlock(length - 10);
199 
200       boolean inFinished = (flags & FLAG_FIN) != 0;
201       boolean outFinished = (flags & FLAG_UNIDIRECTIONAL) != 0;
202       handler.headers(outFinished, inFinished, streamId, associatedStreamId, headerBlock,
203           HeadersMode.SPDY_SYN_STREAM);
204     }
205 
readSynReply(Handler handler, int flags, int length)206     private void readSynReply(Handler handler, int flags, int length) throws IOException {
207       int w1 = source.readInt();
208       int streamId = w1 & 0x7fffffff;
209       List<Header> headerBlock = headerBlockReader.readNameValueBlock(length - 4);
210       boolean inFinished = (flags & FLAG_FIN) != 0;
211       handler.headers(false, inFinished, streamId, -1, headerBlock, HeadersMode.SPDY_REPLY);
212     }
213 
readRstStream(Handler handler, int flags, int length)214     private void readRstStream(Handler handler, int flags, int length) throws IOException {
215       if (length != 8) throw ioException("TYPE_RST_STREAM length: %d != 8", length);
216       int streamId = source.readInt() & 0x7fffffff;
217       int errorCodeInt = source.readInt();
218       ErrorCode errorCode = ErrorCode.fromSpdy3Rst(errorCodeInt);
219       if (errorCode == null) {
220         throw ioException("TYPE_RST_STREAM unexpected error code: %d", errorCodeInt);
221       }
222       handler.rstStream(streamId, errorCode);
223     }
224 
readHeaders(Handler handler, int flags, int length)225     private void readHeaders(Handler handler, int flags, int length) throws IOException {
226       int w1 = source.readInt();
227       int streamId = w1 & 0x7fffffff;
228       List<Header> headerBlock = headerBlockReader.readNameValueBlock(length - 4);
229       handler.headers(false, false, streamId, -1, headerBlock, HeadersMode.SPDY_HEADERS);
230     }
231 
readWindowUpdate(Handler handler, int flags, int length)232     private void readWindowUpdate(Handler handler, int flags, int length) throws IOException {
233       if (length != 8) throw ioException("TYPE_WINDOW_UPDATE length: %d != 8", length);
234       int w1 = source.readInt();
235       int w2 = source.readInt();
236       int streamId = w1 & 0x7fffffff;
237       long increment = w2 & 0x7fffffff;
238       if (increment == 0) throw ioException("windowSizeIncrement was 0", increment);
239       handler.windowUpdate(streamId, increment);
240     }
241 
readPing(Handler handler, int flags, int length)242     private void readPing(Handler handler, int flags, int length) throws IOException {
243       if (length != 4) throw ioException("TYPE_PING length: %d != 4", length);
244       int id = source.readInt();
245       boolean ack = client == ((id & 1) == 1);
246       handler.ping(ack, id, 0);
247     }
248 
readGoAway(Handler handler, int flags, int length)249     private void readGoAway(Handler handler, int flags, int length) throws IOException {
250       if (length != 8) throw ioException("TYPE_GOAWAY length: %d != 8", length);
251       int lastGoodStreamId = source.readInt() & 0x7fffffff;
252       int errorCodeInt = source.readInt();
253       ErrorCode errorCode = ErrorCode.fromSpdyGoAway(errorCodeInt);
254       if (errorCode == null) {
255         throw ioException("TYPE_GOAWAY unexpected error code: %d", errorCodeInt);
256       }
257       handler.goAway(lastGoodStreamId, errorCode, ByteString.EMPTY);
258     }
259 
readSettings(Handler handler, int flags, int length)260     private void readSettings(Handler handler, int flags, int length) throws IOException {
261       int numberOfEntries = source.readInt();
262       if (length != 4 + 8 * numberOfEntries) {
263         throw ioException("TYPE_SETTINGS length: %d != 4 + 8 * %d", length, numberOfEntries);
264       }
265       Settings settings = new Settings();
266       for (int i = 0; i < numberOfEntries; i++) {
267         int w1 = source.readInt();
268         int value = source.readInt();
269         int idFlags = (w1 & 0xff000000) >>> 24;
270         int id = w1 & 0xffffff;
271         settings.set(id, idFlags, value);
272       }
273       boolean clearPrevious = (flags & Settings.FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS) != 0;
274       handler.settings(clearPrevious, settings);
275     }
276 
ioException(String message, Object... args)277     private static IOException ioException(String message, Object... args) throws IOException {
278       throw new IOException(String.format(message, args));
279     }
280 
close()281     @Override public void close() throws IOException {
282       headerBlockReader.close();
283     }
284   }
285 
286   /** Write spdy/3 frames. */
287   static final class Writer implements FrameWriter {
288     private final BufferedSink sink;
289     private final Buffer headerBlockBuffer;
290     private final BufferedSink headerBlockOut;
291     private final boolean client;
292     private boolean closed;
293 
Writer(BufferedSink sink, boolean client)294     Writer(BufferedSink sink, boolean client) {
295       this.sink = sink;
296       this.client = client;
297 
298       Deflater deflater = new Deflater();
299       deflater.setDictionary(DICTIONARY);
300       headerBlockBuffer = new Buffer();
301       headerBlockOut = Okio.buffer(new DeflaterSink(headerBlockBuffer, deflater));
302     }
303 
ackSettings(Settings peerSettings)304     @Override public void ackSettings(Settings peerSettings) {
305       // Do nothing: no ACK for SPDY/3 settings.
306     }
307 
308     @Override
pushPromise(int streamId, int promisedStreamId, List<Header> requestHeaders)309     public void pushPromise(int streamId, int promisedStreamId, List<Header> requestHeaders)
310         throws IOException {
311       // Do nothing: no push promise for SPDY/3.
312     }
313 
connectionPreface()314     @Override public synchronized void connectionPreface() {
315       // Do nothing: no connection preface for SPDY/3.
316     }
317 
flush()318     @Override public synchronized void flush() throws IOException {
319       if (closed) throw new IOException("closed");
320       sink.flush();
321     }
322 
synStream(boolean outFinished, boolean inFinished, int streamId, int associatedStreamId, List<Header> headerBlock)323     @Override public synchronized void synStream(boolean outFinished, boolean inFinished,
324         int streamId, int associatedStreamId, List<Header> headerBlock)
325         throws IOException {
326       if (closed) throw new IOException("closed");
327       writeNameValueBlockToBuffer(headerBlock);
328       int length = (int) (10 + headerBlockBuffer.size());
329       int type = TYPE_SYN_STREAM;
330       int flags = (outFinished ? FLAG_FIN : 0) | (inFinished ? FLAG_UNIDIRECTIONAL : 0);
331 
332       int unused = 0;
333       sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
334       sink.writeInt((flags & 0xff) << 24 | length & 0xffffff);
335       sink.writeInt(streamId & 0x7fffffff);
336       sink.writeInt(associatedStreamId & 0x7fffffff);
337       sink.writeShort((unused & 0x7) << 13 | (unused & 0x1f) << 8 | (unused & 0xff));
338       sink.writeAll(headerBlockBuffer);
339       sink.flush();
340     }
341 
synReply(boolean outFinished, int streamId, List<Header> headerBlock)342     @Override public synchronized void synReply(boolean outFinished, int streamId,
343         List<Header> headerBlock) throws IOException {
344       if (closed) throw new IOException("closed");
345       writeNameValueBlockToBuffer(headerBlock);
346       int type = TYPE_SYN_REPLY;
347       int flags = (outFinished ? FLAG_FIN : 0);
348       int length = (int) (headerBlockBuffer.size() + 4);
349 
350       sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
351       sink.writeInt((flags & 0xff) << 24 | length & 0xffffff);
352       sink.writeInt(streamId & 0x7fffffff);
353       sink.writeAll(headerBlockBuffer);
354       sink.flush();
355     }
356 
headers(int streamId, List<Header> headerBlock)357     @Override public synchronized void headers(int streamId, List<Header> headerBlock)
358         throws IOException {
359       if (closed) throw new IOException("closed");
360       writeNameValueBlockToBuffer(headerBlock);
361       int flags = 0;
362       int type = TYPE_HEADERS;
363       int length = (int) (headerBlockBuffer.size() + 4);
364 
365       sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
366       sink.writeInt((flags & 0xff) << 24 | length & 0xffffff);
367       sink.writeInt(streamId & 0x7fffffff);
368       sink.writeAll(headerBlockBuffer);
369     }
370 
rstStream(int streamId, ErrorCode errorCode)371     @Override public synchronized void rstStream(int streamId, ErrorCode errorCode)
372         throws IOException {
373       if (closed) throw new IOException("closed");
374       if (errorCode.spdyRstCode == -1) throw new IllegalArgumentException();
375       int flags = 0;
376       int type = TYPE_RST_STREAM;
377       int length = 8;
378       sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
379       sink.writeInt((flags & 0xff) << 24 | length & 0xffffff);
380       sink.writeInt(streamId & 0x7fffffff);
381       sink.writeInt(errorCode.spdyRstCode);
382       sink.flush();
383     }
384 
maxDataLength()385     @Override public int maxDataLength() {
386       return 16383;
387     }
388 
data(boolean outFinished, int streamId, Buffer source, int byteCount)389     @Override public synchronized void data(boolean outFinished, int streamId, Buffer source,
390         int byteCount) throws IOException {
391       int flags = (outFinished ? FLAG_FIN : 0);
392       sendDataFrame(streamId, flags, source, byteCount);
393     }
394 
sendDataFrame(int streamId, int flags, Buffer buffer, int byteCount)395     void sendDataFrame(int streamId, int flags, Buffer buffer, int byteCount)
396         throws IOException {
397       if (closed) throw new IOException("closed");
398       if (byteCount > 0xffffffL) {
399         throw new IllegalArgumentException("FRAME_TOO_LARGE max size is 16Mib: " + byteCount);
400       }
401       sink.writeInt(streamId & 0x7fffffff);
402       sink.writeInt((flags & 0xff) << 24 | byteCount & 0xffffff);
403       if (byteCount > 0) {
404         sink.write(buffer, byteCount);
405       }
406     }
407 
writeNameValueBlockToBuffer(List<Header> headerBlock)408     private void writeNameValueBlockToBuffer(List<Header> headerBlock) throws IOException {
409       headerBlockOut.writeInt(headerBlock.size());
410       for (int i = 0, size = headerBlock.size(); i < size; i++) {
411         ByteString name = headerBlock.get(i).name;
412         headerBlockOut.writeInt(name.size());
413         headerBlockOut.write(name);
414         ByteString value = headerBlock.get(i).value;
415         headerBlockOut.writeInt(value.size());
416         headerBlockOut.write(value);
417       }
418       headerBlockOut.flush();
419     }
420 
settings(Settings settings)421     @Override public synchronized void settings(Settings settings) throws IOException {
422       if (closed) throw new IOException("closed");
423       int type = TYPE_SETTINGS;
424       int flags = 0;
425       int size = settings.size();
426       int length = 4 + size * 8;
427       sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
428       sink.writeInt((flags & 0xff) << 24 | length & 0xffffff);
429       sink.writeInt(size);
430       for (int i = 0; i <= Settings.COUNT; i++) {
431         if (!settings.isSet(i)) continue;
432         int settingsFlags = settings.flags(i);
433         sink.writeInt((settingsFlags & 0xff) << 24 | (i & 0xffffff));
434         sink.writeInt(settings.get(i));
435       }
436       sink.flush();
437     }
438 
ping(boolean reply, int payload1, int payload2)439     @Override public synchronized void ping(boolean reply, int payload1, int payload2)
440         throws IOException {
441       if (closed) throw new IOException("closed");
442       boolean payloadIsReply = client != ((payload1 & 1) == 1);
443       if (reply != payloadIsReply) throw new IllegalArgumentException("payload != reply");
444       int type = TYPE_PING;
445       int flags = 0;
446       int length = 4;
447       sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
448       sink.writeInt((flags & 0xff) << 24 | length & 0xffffff);
449       sink.writeInt(payload1);
450       sink.flush();
451     }
452 
goAway(int lastGoodStreamId, ErrorCode errorCode, byte[] ignored)453     @Override public synchronized void goAway(int lastGoodStreamId, ErrorCode errorCode,
454         byte[] ignored) throws IOException {
455       if (closed) throw new IOException("closed");
456       if (errorCode.spdyGoAwayCode == -1) {
457         throw new IllegalArgumentException("errorCode.spdyGoAwayCode == -1");
458       }
459       int type = TYPE_GOAWAY;
460       int flags = 0;
461       int length = 8;
462       sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
463       sink.writeInt((flags & 0xff) << 24 | length & 0xffffff);
464       sink.writeInt(lastGoodStreamId);
465       sink.writeInt(errorCode.spdyGoAwayCode);
466       sink.flush();
467     }
468 
windowUpdate(int streamId, long increment)469     @Override public synchronized void windowUpdate(int streamId, long increment)
470         throws IOException {
471       if (closed) throw new IOException("closed");
472       if (increment == 0 || increment > 0x7fffffffL) {
473         throw new IllegalArgumentException(
474             "windowSizeIncrement must be between 1 and 0x7fffffff: " + increment);
475       }
476       int type = TYPE_WINDOW_UPDATE;
477       int flags = 0;
478       int length = 8;
479       sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff);
480       sink.writeInt((flags & 0xff) << 24 | length & 0xffffff);
481       sink.writeInt(streamId);
482       sink.writeInt((int) increment);
483       sink.flush();
484     }
485 
close()486     @Override public synchronized void close() throws IOException {
487       closed = true;
488       Util.closeAll(sink, headerBlockOut);
489     }
490   }
491 }
492