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