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.spdy; 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.BufferedSink; 26 import okio.BufferedSource; 27 import okio.ByteString; 28 import okio.DeflaterSink; 29 import okio.OkBuffer; 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 final class Spdy3 implements Variant { 37 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 98 @Override public FrameReader newReader(BufferedSource source, boolean client) { 99 return new Reader(source, client); 100 } 101 102 @Override public FrameWriter newWriter(BufferedSink sink, boolean client) { 103 return new Writer(sink, client); 104 } 105 106 @Override public int maxFrameSize() { 107 return 16383; 108 } 109 110 /** Read spdy/3 frames. */ 111 static final class Reader implements FrameReader { 112 private final BufferedSource source; 113 private final boolean client; 114 private final NameValueBlockReader headerBlockReader; 115 116 Reader(BufferedSource source, boolean client) { 117 this.source = source; 118 this.headerBlockReader = new NameValueBlockReader(this.source); 119 this.client = client; 120 } 121 122 @Override public void readConnectionHeader() { 123 } 124 125 /** 126 * Send the next frame to {@code handler}. Returns true unless there are no 127 * more frames on the stream. 128 */ 129 @Override public boolean nextFrame(Handler handler) throws IOException { 130 int w1; 131 int w2; 132 try { 133 w1 = source.readInt(); 134 w2 = source.readInt(); 135 } catch (IOException e) { 136 return false; // This might be a normal socket close. 137 } 138 139 boolean control = (w1 & 0x80000000) != 0; 140 int flags = (w2 & 0xff000000) >>> 24; 141 int length = (w2 & 0xffffff); 142 143 if (control) { 144 int version = (w1 & 0x7fff0000) >>> 16; 145 int type = (w1 & 0xffff); 146 147 if (version != 3) { 148 throw new ProtocolException("version != 3: " + version); 149 } 150 151 switch (type) { 152 case TYPE_SYN_STREAM: 153 readSynStream(handler, flags, length); 154 return true; 155 156 case TYPE_SYN_REPLY: 157 readSynReply(handler, flags, length); 158 return true; 159 160 case TYPE_RST_STREAM: 161 readRstStream(handler, flags, length); 162 return true; 163 164 case TYPE_SETTINGS: 165 readSettings(handler, flags, length); 166 return true; 167 168 case TYPE_PING: 169 readPing(handler, flags, length); 170 return true; 171 172 case TYPE_GOAWAY: 173 readGoAway(handler, flags, length); 174 return true; 175 176 case TYPE_HEADERS: 177 readHeaders(handler, flags, length); 178 return true; 179 180 case TYPE_WINDOW_UPDATE: 181 readWindowUpdate(handler, flags, length); 182 return true; 183 184 default: 185 source.skip(length); 186 return true; 187 } 188 } else { 189 int streamId = w1 & 0x7fffffff; 190 boolean inFinished = (flags & FLAG_FIN) != 0; 191 handler.data(inFinished, streamId, source, length); 192 return true; 193 } 194 } 195 196 private void readSynStream(Handler handler, int flags, int length) throws IOException { 197 int w1 = source.readInt(); 198 int w2 = source.readInt(); 199 int s3 = source.readShort(); 200 int streamId = w1 & 0x7fffffff; 201 int associatedStreamId = w2 & 0x7fffffff; 202 int priority = (s3 & 0xe000) >>> 13; 203 // int slot = s3 & 0xff; 204 List<Header> headerBlock = headerBlockReader.readNameValueBlock(length - 10); 205 206 boolean inFinished = (flags & FLAG_FIN) != 0; 207 boolean outFinished = (flags & FLAG_UNIDIRECTIONAL) != 0; 208 handler.headers(outFinished, inFinished, streamId, associatedStreamId, priority, 209 headerBlock, HeadersMode.SPDY_SYN_STREAM); 210 } 211 212 private void readSynReply(Handler handler, int flags, int length) throws IOException { 213 int w1 = source.readInt(); 214 int streamId = w1 & 0x7fffffff; 215 List<Header> headerBlock = headerBlockReader.readNameValueBlock(length - 4); 216 boolean inFinished = (flags & FLAG_FIN) != 0; 217 handler.headers(false, inFinished, streamId, -1, -1, headerBlock, HeadersMode.SPDY_REPLY); 218 } 219 220 private void readRstStream(Handler handler, int flags, int length) throws IOException { 221 if (length != 8) throw ioException("TYPE_RST_STREAM length: %d != 8", length); 222 int streamId = source.readInt() & 0x7fffffff; 223 int errorCodeInt = source.readInt(); 224 ErrorCode errorCode = ErrorCode.fromSpdy3Rst(errorCodeInt); 225 if (errorCode == null) { 226 throw ioException("TYPE_RST_STREAM unexpected error code: %d", errorCodeInt); 227 } 228 handler.rstStream(streamId, errorCode); 229 } 230 231 private void readHeaders(Handler handler, int flags, int length) throws IOException { 232 int w1 = source.readInt(); 233 int streamId = w1 & 0x7fffffff; 234 List<Header> headerBlock = headerBlockReader.readNameValueBlock(length - 4); 235 handler.headers(false, false, streamId, -1, -1, headerBlock, HeadersMode.SPDY_HEADERS); 236 } 237 238 private void readWindowUpdate(Handler handler, int flags, int length) throws IOException { 239 if (length != 8) throw ioException("TYPE_WINDOW_UPDATE length: %d != 8", length); 240 int w1 = source.readInt(); 241 int w2 = source.readInt(); 242 int streamId = w1 & 0x7fffffff; 243 long increment = w2 & 0x7fffffff; 244 if (increment == 0) throw ioException("windowSizeIncrement was 0", increment); 245 handler.windowUpdate(streamId, increment); 246 } 247 248 private void readPing(Handler handler, int flags, int length) throws IOException { 249 if (length != 4) throw ioException("TYPE_PING length: %d != 4", length); 250 int id = source.readInt(); 251 boolean ack = client == ((id & 1) == 1); 252 handler.ping(ack, id, 0); 253 } 254 255 private void readGoAway(Handler handler, int flags, int length) throws IOException { 256 if (length != 8) throw ioException("TYPE_GOAWAY length: %d != 8", length); 257 int lastGoodStreamId = source.readInt() & 0x7fffffff; 258 int errorCodeInt = source.readInt(); 259 ErrorCode errorCode = ErrorCode.fromSpdyGoAway(errorCodeInt); 260 if (errorCode == null) { 261 throw ioException("TYPE_GOAWAY unexpected error code: %d", errorCodeInt); 262 } 263 handler.goAway(lastGoodStreamId, errorCode, ByteString.EMPTY); 264 } 265 266 private void readSettings(Handler handler, int flags, int length) throws IOException { 267 int numberOfEntries = source.readInt(); 268 if (length != 4 + 8 * numberOfEntries) { 269 throw ioException("TYPE_SETTINGS length: %d != 4 + 8 * %d", length, numberOfEntries); 270 } 271 Settings settings = new Settings(); 272 for (int i = 0; i < numberOfEntries; i++) { 273 int w1 = source.readInt(); 274 int value = source.readInt(); 275 int idFlags = (w1 & 0xff000000) >>> 24; 276 int id = w1 & 0xffffff; 277 settings.set(id, idFlags, value); 278 } 279 boolean clearPrevious = (flags & Settings.FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS) != 0; 280 handler.settings(clearPrevious, settings); 281 } 282 283 private static IOException ioException(String message, Object... args) throws IOException { 284 throw new IOException(String.format(message, args)); 285 } 286 287 @Override public void close() throws IOException { 288 headerBlockReader.close(); 289 } 290 } 291 292 /** Write spdy/3 frames. */ 293 static final class Writer implements FrameWriter { 294 private final BufferedSink sink; 295 private final OkBuffer headerBlockBuffer; 296 private final BufferedSink headerBlockOut; 297 private final boolean client; 298 private boolean closed; 299 300 Writer(BufferedSink sink, boolean client) { 301 this.sink = sink; 302 this.client = client; 303 304 Deflater deflater = new Deflater(); 305 deflater.setDictionary(DICTIONARY); 306 headerBlockBuffer = new OkBuffer(); 307 headerBlockOut = Okio.buffer(new DeflaterSink(headerBlockBuffer, deflater)); 308 } 309 310 @Override public void ackSettings() { 311 // Do nothing: no ACK for SPDY/3 settings. 312 } 313 314 @Override 315 public void pushPromise(int streamId, int promisedStreamId, List<Header> requestHeaders) 316 throws IOException { 317 // Do nothing: no push promise for SPDY/3. 318 } 319 320 @Override public synchronized void connectionHeader() { 321 // Do nothing: no connection header for SPDY/3. 322 } 323 324 @Override public synchronized void flush() throws IOException { 325 if (closed) throw new IOException("closed"); 326 sink.flush(); 327 } 328 329 @Override public synchronized void synStream(boolean outFinished, boolean inFinished, 330 int streamId, int associatedStreamId, int priority, int slot, List<Header> headerBlock) 331 throws IOException { 332 if (closed) throw new IOException("closed"); 333 writeNameValueBlockToBuffer(headerBlock); 334 int length = (int) (10 + headerBlockBuffer.size()); 335 int type = TYPE_SYN_STREAM; 336 int flags = (outFinished ? FLAG_FIN : 0) | (inFinished ? FLAG_UNIDIRECTIONAL : 0); 337 338 int unused = 0; 339 sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff); 340 sink.writeInt((flags & 0xff) << 24 | length & 0xffffff); 341 sink.writeInt(streamId & 0x7fffffff); 342 sink.writeInt(associatedStreamId & 0x7fffffff); 343 sink.writeShort((priority & 0x7) << 13 | (unused & 0x1f) << 8 | (slot & 0xff)); 344 sink.write(headerBlockBuffer, headerBlockBuffer.size()); 345 sink.flush(); 346 } 347 348 @Override public synchronized void synReply(boolean outFinished, int streamId, 349 List<Header> headerBlock) throws IOException { 350 if (closed) throw new IOException("closed"); 351 writeNameValueBlockToBuffer(headerBlock); 352 int type = TYPE_SYN_REPLY; 353 int flags = (outFinished ? FLAG_FIN : 0); 354 int length = (int) (headerBlockBuffer.size() + 4); 355 356 sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff); 357 sink.writeInt((flags & 0xff) << 24 | length & 0xffffff); 358 sink.writeInt(streamId & 0x7fffffff); 359 sink.write(headerBlockBuffer, headerBlockBuffer.size()); 360 sink.flush(); 361 } 362 363 @Override public synchronized void headers(int streamId, List<Header> headerBlock) 364 throws IOException { 365 if (closed) throw new IOException("closed"); 366 writeNameValueBlockToBuffer(headerBlock); 367 int flags = 0; 368 int type = TYPE_HEADERS; 369 int length = (int) (headerBlockBuffer.size() + 4); 370 371 sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff); 372 sink.writeInt((flags & 0xff) << 24 | length & 0xffffff); 373 sink.writeInt(streamId & 0x7fffffff); 374 sink.write(headerBlockBuffer, headerBlockBuffer.size()); 375 } 376 377 @Override public synchronized void rstStream(int streamId, ErrorCode errorCode) 378 throws IOException { 379 if (closed) throw new IOException("closed"); 380 if (errorCode.spdyRstCode == -1) throw new IllegalArgumentException(); 381 int flags = 0; 382 int type = TYPE_RST_STREAM; 383 int length = 8; 384 sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff); 385 sink.writeInt((flags & 0xff) << 24 | length & 0xffffff); 386 sink.writeInt(streamId & 0x7fffffff); 387 sink.writeInt(errorCode.spdyRstCode); 388 sink.flush(); 389 } 390 391 @Override public synchronized void data(boolean outFinished, int streamId, OkBuffer source) 392 throws IOException { 393 data(outFinished, streamId, source, (int) source.size()); 394 } 395 396 @Override public synchronized void data(boolean outFinished, int streamId, OkBuffer source, 397 int byteCount) throws IOException { 398 int flags = (outFinished ? FLAG_FIN : 0); 399 sendDataFrame(streamId, flags, source, byteCount); 400 } 401 402 void sendDataFrame(int streamId, int flags, OkBuffer buffer, int byteCount) 403 throws IOException { 404 if (closed) throw new IOException("closed"); 405 if (byteCount > 0xffffffL) { 406 throw new IllegalArgumentException("FRAME_TOO_LARGE max size is 16Mib: " + byteCount); 407 } 408 sink.writeInt(streamId & 0x7fffffff); 409 sink.writeInt((flags & 0xff) << 24 | byteCount & 0xffffff); 410 if (byteCount > 0) { 411 sink.write(buffer, byteCount); 412 } 413 } 414 415 private void writeNameValueBlockToBuffer(List<Header> headerBlock) throws IOException { 416 if (headerBlockBuffer.size() != 0) throw new IllegalStateException(); 417 headerBlockOut.writeInt(headerBlock.size()); 418 for (int i = 0, size = headerBlock.size(); i < size; i++) { 419 ByteString name = headerBlock.get(i).name; 420 headerBlockOut.writeInt(name.size()); 421 headerBlockOut.write(name); 422 ByteString value = headerBlock.get(i).value; 423 headerBlockOut.writeInt(value.size()); 424 headerBlockOut.write(value); 425 } 426 headerBlockOut.flush(); 427 } 428 429 @Override public synchronized void settings(Settings settings) throws IOException { 430 if (closed) throw new IOException("closed"); 431 int type = TYPE_SETTINGS; 432 int flags = 0; 433 int size = settings.size(); 434 int length = 4 + size * 8; 435 sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff); 436 sink.writeInt((flags & 0xff) << 24 | length & 0xffffff); 437 sink.writeInt(size); 438 for (int i = 0; i <= Settings.COUNT; i++) { 439 if (!settings.isSet(i)) continue; 440 int settingsFlags = settings.flags(i); 441 sink.writeInt((settingsFlags & 0xff) << 24 | (i & 0xffffff)); 442 sink.writeInt(settings.get(i)); 443 } 444 sink.flush(); 445 } 446 447 @Override public synchronized void ping(boolean reply, int payload1, int payload2) 448 throws IOException { 449 if (closed) throw new IOException("closed"); 450 boolean payloadIsReply = client != ((payload1 & 1) == 1); 451 if (reply != payloadIsReply) throw new IllegalArgumentException("payload != reply"); 452 int type = TYPE_PING; 453 int flags = 0; 454 int length = 4; 455 sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff); 456 sink.writeInt((flags & 0xff) << 24 | length & 0xffffff); 457 sink.writeInt(payload1); 458 sink.flush(); 459 } 460 461 @Override public synchronized void goAway(int lastGoodStreamId, ErrorCode errorCode, 462 byte[] ignored) throws IOException { 463 if (closed) throw new IOException("closed"); 464 if (errorCode.spdyGoAwayCode == -1) { 465 throw new IllegalArgumentException("errorCode.spdyGoAwayCode == -1"); 466 } 467 int type = TYPE_GOAWAY; 468 int flags = 0; 469 int length = 8; 470 sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff); 471 sink.writeInt((flags & 0xff) << 24 | length & 0xffffff); 472 sink.writeInt(lastGoodStreamId); 473 sink.writeInt(errorCode.spdyGoAwayCode); 474 sink.flush(); 475 } 476 477 @Override public synchronized void windowUpdate(int streamId, long increment) 478 throws IOException { 479 if (closed) throw new IOException("closed"); 480 if (increment == 0 || increment > 0x7fffffffL) { 481 throw new IllegalArgumentException( 482 "windowSizeIncrement must be between 1 and 0x7fffffff: " + increment); 483 } 484 int type = TYPE_WINDOW_UPDATE; 485 int flags = 0; 486 int length = 8; 487 sink.writeInt(0x80000000 | (VERSION & 0x7fff) << 16 | type & 0xffff); 488 sink.writeInt((flags & 0xff) << 24 | length & 0xffffff); 489 sink.writeInt(streamId); 490 sink.writeInt((int) increment); 491 sink.flush(); 492 } 493 494 @Override public synchronized void close() throws IOException { 495 closed = true; 496 Util.closeAll(sink, headerBlockOut); 497 } 498 } 499 } 500