1 /* 2 * Copyright (C) 2013 Square, Inc. 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 java.io.IOException; 20 import java.util.List; 21 import java.util.logging.Logger; 22 import okio.Buffer; 23 import okio.BufferedSink; 24 import okio.BufferedSource; 25 import okio.ByteString; 26 import okio.Source; 27 import okio.Timeout; 28 29 import static com.squareup.okhttp.internal.spdy.Http2.FrameLogger.formatHeader; 30 import static java.lang.String.format; 31 import static java.util.logging.Level.FINE; 32 import static okio.ByteString.EMPTY; 33 34 /** 35 * Read and write HTTP/2 frames. 36 * <p> 37 * This implementation assumes we do not send an increased 38 * {@link Settings#getMaxFrameSize frame size setting} to the peer. Hence, we 39 * expect all frames to have a max length of {@link #INITIAL_MAX_FRAME_SIZE}. 40 * <p>http://tools.ietf.org/html/draft-ietf-httpbis-http2-17 41 */ 42 public final class Http2 implements Variant { 43 private static final Logger logger = Logger.getLogger(FrameLogger.class.getName()); 44 getProtocol()45 @Override public Protocol getProtocol() { 46 return Protocol.HTTP_2; 47 } 48 49 private static final ByteString CONNECTION_PREFACE 50 = ByteString.encodeUtf8("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"); 51 52 /** The initial max frame size, applied independently writing to, or reading from the peer. */ 53 static final int INITIAL_MAX_FRAME_SIZE = 0x4000; // 16384 54 55 static final byte TYPE_DATA = 0x0; 56 static final byte TYPE_HEADERS = 0x1; 57 static final byte TYPE_PRIORITY = 0x2; 58 static final byte TYPE_RST_STREAM = 0x3; 59 static final byte TYPE_SETTINGS = 0x4; 60 static final byte TYPE_PUSH_PROMISE = 0x5; 61 static final byte TYPE_PING = 0x6; 62 static final byte TYPE_GOAWAY = 0x7; 63 static final byte TYPE_WINDOW_UPDATE = 0x8; 64 static final byte TYPE_CONTINUATION = 0x9; 65 66 static final byte FLAG_NONE = 0x0; 67 static final byte FLAG_ACK = 0x1; // Used for settings and ping. 68 static final byte FLAG_END_STREAM = 0x1; // Used for headers and data. 69 static final byte FLAG_END_HEADERS = 0x4; // Used for headers and continuation. 70 static final byte FLAG_END_PUSH_PROMISE = 0x4; 71 static final byte FLAG_PADDED = 0x8; // Used for headers and data. 72 static final byte FLAG_PRIORITY = 0x20; // Used for headers. 73 static final byte FLAG_COMPRESSED = 0x20; // Used for data. 74 75 /** 76 * Creates a frame reader with max header table size of 4096 and data frame 77 * compression disabled. 78 */ newReader(BufferedSource source, boolean client)79 @Override public FrameReader newReader(BufferedSource source, boolean client) { 80 return new Reader(source, 4096, client); 81 } 82 newWriter(BufferedSink sink, boolean client)83 @Override public FrameWriter newWriter(BufferedSink sink, boolean client) { 84 return new Writer(sink, client); 85 } 86 87 static final class Reader implements FrameReader { 88 private final BufferedSource source; 89 private final ContinuationSource continuation; 90 private final boolean client; 91 92 // Visible for testing. 93 final Hpack.Reader hpackReader; 94 Reader(BufferedSource source, int headerTableSize, boolean client)95 Reader(BufferedSource source, int headerTableSize, boolean client) { 96 this.source = source; 97 this.client = client; 98 this.continuation = new ContinuationSource(this.source); 99 this.hpackReader = new Hpack.Reader(headerTableSize, continuation); 100 } 101 readConnectionPreface()102 @Override public void readConnectionPreface() throws IOException { 103 if (client) return; // Nothing to read; servers doesn't send a connection preface! 104 ByteString connectionPreface = source.readByteString(CONNECTION_PREFACE.size()); 105 if (logger.isLoggable(FINE)) logger.fine(format("<< CONNECTION %s", connectionPreface.hex())); 106 if (!CONNECTION_PREFACE.equals(connectionPreface)) { 107 throw ioException("Expected a connection header but was %s", connectionPreface.utf8()); 108 } 109 } 110 nextFrame(Handler handler)111 @Override public boolean nextFrame(Handler handler) throws IOException { 112 try { 113 source.require(9); // Frame header size 114 } catch (IOException e) { 115 return false; // This might be a normal socket close. 116 } 117 118 /* 0 1 2 3 119 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 120 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 121 * | Length (24) | 122 * +---------------+---------------+---------------+ 123 * | Type (8) | Flags (8) | 124 * +-+-+-----------+---------------+-------------------------------+ 125 * |R| Stream Identifier (31) | 126 * +=+=============================================================+ 127 * | Frame Payload (0...) ... 128 * +---------------------------------------------------------------+ 129 */ 130 int length = readMedium(source); 131 if (length < 0 || length > INITIAL_MAX_FRAME_SIZE) { 132 throw ioException("FRAME_SIZE_ERROR: %s", length); 133 } 134 byte type = (byte) (source.readByte() & 0xff); 135 byte flags = (byte) (source.readByte() & 0xff); 136 int streamId = (source.readInt() & 0x7fffffff); // Ignore reserved bit. 137 if (logger.isLoggable(FINE)) logger.fine(formatHeader(true, streamId, length, type, flags)); 138 139 switch (type) { 140 case TYPE_DATA: 141 readData(handler, length, flags, streamId); 142 break; 143 144 case TYPE_HEADERS: 145 readHeaders(handler, length, flags, streamId); 146 break; 147 148 case TYPE_PRIORITY: 149 readPriority(handler, length, flags, streamId); 150 break; 151 152 case TYPE_RST_STREAM: 153 readRstStream(handler, length, flags, streamId); 154 break; 155 156 case TYPE_SETTINGS: 157 readSettings(handler, length, flags, streamId); 158 break; 159 160 case TYPE_PUSH_PROMISE: 161 readPushPromise(handler, length, flags, streamId); 162 break; 163 164 case TYPE_PING: 165 readPing(handler, length, flags, streamId); 166 break; 167 168 case TYPE_GOAWAY: 169 readGoAway(handler, length, flags, streamId); 170 break; 171 172 case TYPE_WINDOW_UPDATE: 173 readWindowUpdate(handler, length, flags, streamId); 174 break; 175 176 default: 177 // Implementations MUST discard frames that have unknown or unsupported types. 178 source.skip(length); 179 } 180 return true; 181 } 182 readHeaders(Handler handler, int length, byte flags, int streamId)183 private void readHeaders(Handler handler, int length, byte flags, int streamId) 184 throws IOException { 185 if (streamId == 0) throw ioException("PROTOCOL_ERROR: TYPE_HEADERS streamId == 0"); 186 187 boolean endStream = (flags & FLAG_END_STREAM) != 0; 188 189 short padding = (flags & FLAG_PADDED) != 0 ? (short) (source.readByte() & 0xff) : 0; 190 191 if ((flags & FLAG_PRIORITY) != 0) { 192 readPriority(handler, streamId); 193 length -= 5; // account for above read. 194 } 195 196 length = lengthWithoutPadding(length, flags, padding); 197 198 List<Header> headerBlock = readHeaderBlock(length, padding, flags, streamId); 199 200 handler.headers(false, endStream, streamId, -1, headerBlock, HeadersMode.HTTP_20_HEADERS); 201 } 202 readHeaderBlock(int length, short padding, byte flags, int streamId)203 private List<Header> readHeaderBlock(int length, short padding, byte flags, int streamId) 204 throws IOException { 205 continuation.length = continuation.left = length; 206 continuation.padding = padding; 207 continuation.flags = flags; 208 continuation.streamId = streamId; 209 210 // TODO: Concat multi-value headers with 0x0, except COOKIE, which uses 0x3B, 0x20. 211 // http://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8.1.2.5 212 hpackReader.readHeaders(); 213 return hpackReader.getAndResetHeaderList(); 214 } 215 readData(Handler handler, int length, byte flags, int streamId)216 private void readData(Handler handler, int length, byte flags, int streamId) 217 throws IOException { 218 // TODO: checkState open or half-closed (local) or raise STREAM_CLOSED 219 boolean inFinished = (flags & FLAG_END_STREAM) != 0; 220 boolean gzipped = (flags & FLAG_COMPRESSED) != 0; 221 if (gzipped) { 222 throw ioException("PROTOCOL_ERROR: FLAG_COMPRESSED without SETTINGS_COMPRESS_DATA"); 223 } 224 225 short padding = (flags & FLAG_PADDED) != 0 ? (short) (source.readByte() & 0xff) : 0; 226 length = lengthWithoutPadding(length, flags, padding); 227 228 handler.data(inFinished, streamId, source, length); 229 source.skip(padding); 230 } 231 readPriority(Handler handler, int length, byte flags, int streamId)232 private void readPriority(Handler handler, int length, byte flags, int streamId) 233 throws IOException { 234 if (length != 5) throw ioException("TYPE_PRIORITY length: %d != 5", length); 235 if (streamId == 0) throw ioException("TYPE_PRIORITY streamId == 0"); 236 readPriority(handler, streamId); 237 } 238 readPriority(Handler handler, int streamId)239 private void readPriority(Handler handler, int streamId) throws IOException { 240 int w1 = source.readInt(); 241 boolean exclusive = (w1 & 0x80000000) != 0; 242 int streamDependency = (w1 & 0x7fffffff); 243 int weight = (source.readByte() & 0xff) + 1; 244 handler.priority(streamId, streamDependency, weight, exclusive); 245 } 246 readRstStream(Handler handler, int length, byte flags, int streamId)247 private void readRstStream(Handler handler, int length, byte flags, int streamId) 248 throws IOException { 249 if (length != 4) throw ioException("TYPE_RST_STREAM length: %d != 4", length); 250 if (streamId == 0) throw ioException("TYPE_RST_STREAM streamId == 0"); 251 int errorCodeInt = source.readInt(); 252 ErrorCode errorCode = ErrorCode.fromHttp2(errorCodeInt); 253 if (errorCode == null) { 254 throw ioException("TYPE_RST_STREAM unexpected error code: %d", errorCodeInt); 255 } 256 handler.rstStream(streamId, errorCode); 257 } 258 readSettings(Handler handler, int length, byte flags, int streamId)259 private void readSettings(Handler handler, int length, byte flags, int streamId) 260 throws IOException { 261 if (streamId != 0) throw ioException("TYPE_SETTINGS streamId != 0"); 262 if ((flags & FLAG_ACK) != 0) { 263 if (length != 0) throw ioException("FRAME_SIZE_ERROR ack frame should be empty!"); 264 handler.ackSettings(); 265 return; 266 } 267 268 if (length % 6 != 0) throw ioException("TYPE_SETTINGS length %% 6 != 0: %s", length); 269 Settings settings = new Settings(); 270 for (int i = 0; i < length; i += 6) { 271 short id = source.readShort(); 272 int value = source.readInt(); 273 274 switch (id) { 275 case 1: // SETTINGS_HEADER_TABLE_SIZE 276 break; 277 case 2: // SETTINGS_ENABLE_PUSH 278 if (value != 0 && value != 1) { 279 throw ioException("PROTOCOL_ERROR SETTINGS_ENABLE_PUSH != 0 or 1"); 280 } 281 break; 282 case 3: // SETTINGS_MAX_CONCURRENT_STREAMS 283 id = 4; // Renumbered in draft 10. 284 break; 285 case 4: // SETTINGS_INITIAL_WINDOW_SIZE 286 id = 7; // Renumbered in draft 10. 287 if (value < 0) { 288 throw ioException("PROTOCOL_ERROR SETTINGS_INITIAL_WINDOW_SIZE > 2^31 - 1"); 289 } 290 break; 291 case 5: // SETTINGS_MAX_FRAME_SIZE 292 if (value < INITIAL_MAX_FRAME_SIZE || value > 16777215) { 293 throw ioException("PROTOCOL_ERROR SETTINGS_MAX_FRAME_SIZE: %s", value); 294 } 295 break; 296 case 6: // SETTINGS_MAX_HEADER_LIST_SIZE 297 break; // Advisory only, so ignored. 298 default: 299 throw ioException("PROTOCOL_ERROR invalid settings id: %s", id); 300 } 301 settings.set(id, 0, value); 302 } 303 handler.settings(false, settings); 304 if (settings.getHeaderTableSize() >= 0) { 305 hpackReader.headerTableSizeSetting(settings.getHeaderTableSize()); 306 } 307 } 308 readPushPromise(Handler handler, int length, byte flags, int streamId)309 private void readPushPromise(Handler handler, int length, byte flags, int streamId) 310 throws IOException { 311 if (streamId == 0) { 312 throw ioException("PROTOCOL_ERROR: TYPE_PUSH_PROMISE streamId == 0"); 313 } 314 short padding = (flags & FLAG_PADDED) != 0 ? (short) (source.readByte() & 0xff) : 0; 315 int promisedStreamId = source.readInt() & 0x7fffffff; 316 length -= 4; // account for above read. 317 length = lengthWithoutPadding(length, flags, padding); 318 List<Header> headerBlock = readHeaderBlock(length, padding, flags, streamId); 319 handler.pushPromise(streamId, promisedStreamId, headerBlock); 320 } 321 readPing(Handler handler, int length, byte flags, int streamId)322 private void readPing(Handler handler, int length, byte flags, int streamId) 323 throws IOException { 324 if (length != 8) throw ioException("TYPE_PING length != 8: %s", length); 325 if (streamId != 0) throw ioException("TYPE_PING streamId != 0"); 326 int payload1 = source.readInt(); 327 int payload2 = source.readInt(); 328 boolean ack = (flags & FLAG_ACK) != 0; 329 handler.ping(ack, payload1, payload2); 330 } 331 readGoAway(Handler handler, int length, byte flags, int streamId)332 private void readGoAway(Handler handler, int length, byte flags, int streamId) 333 throws IOException { 334 if (length < 8) throw ioException("TYPE_GOAWAY length < 8: %s", length); 335 if (streamId != 0) throw ioException("TYPE_GOAWAY streamId != 0"); 336 int lastStreamId = source.readInt(); 337 int errorCodeInt = source.readInt(); 338 int opaqueDataLength = length - 8; 339 ErrorCode errorCode = ErrorCode.fromHttp2(errorCodeInt); 340 if (errorCode == null) { 341 throw ioException("TYPE_GOAWAY unexpected error code: %d", errorCodeInt); 342 } 343 ByteString debugData = EMPTY; 344 if (opaqueDataLength > 0) { // Must read debug data in order to not corrupt the connection. 345 debugData = source.readByteString(opaqueDataLength); 346 } 347 handler.goAway(lastStreamId, errorCode, debugData); 348 } 349 readWindowUpdate(Handler handler, int length, byte flags, int streamId)350 private void readWindowUpdate(Handler handler, int length, byte flags, int streamId) 351 throws IOException { 352 if (length != 4) throw ioException("TYPE_WINDOW_UPDATE length !=4: %s", length); 353 long increment = (source.readInt() & 0x7fffffffL); 354 if (increment == 0) throw ioException("windowSizeIncrement was 0", increment); 355 handler.windowUpdate(streamId, increment); 356 } 357 close()358 @Override public void close() throws IOException { 359 source.close(); 360 } 361 } 362 363 static final class Writer implements FrameWriter { 364 private final BufferedSink sink; 365 private final boolean client; 366 private final Buffer hpackBuffer; 367 private final Hpack.Writer hpackWriter; 368 private int maxFrameSize; 369 private boolean closed; 370 Writer(BufferedSink sink, boolean client)371 Writer(BufferedSink sink, boolean client) { 372 this.sink = sink; 373 this.client = client; 374 this.hpackBuffer = new Buffer(); 375 this.hpackWriter = new Hpack.Writer(hpackBuffer); 376 this.maxFrameSize = INITIAL_MAX_FRAME_SIZE; 377 } 378 flush()379 @Override public synchronized void flush() throws IOException { 380 if (closed) throw new IOException("closed"); 381 sink.flush(); 382 } 383 ackSettings(Settings peerSettings)384 @Override public synchronized void ackSettings(Settings peerSettings) throws IOException { 385 if (closed) throw new IOException("closed"); 386 this.maxFrameSize = peerSettings.getMaxFrameSize(maxFrameSize); 387 int length = 0; 388 byte type = TYPE_SETTINGS; 389 byte flags = FLAG_ACK; 390 int streamId = 0; 391 frameHeader(streamId, length, type, flags); 392 sink.flush(); 393 } 394 connectionPreface()395 @Override public synchronized void connectionPreface() throws IOException { 396 if (closed) throw new IOException("closed"); 397 if (!client) return; // Nothing to write; servers don't send connection headers! 398 if (logger.isLoggable(FINE)) { 399 logger.fine(format(">> CONNECTION %s", CONNECTION_PREFACE.hex())); 400 } 401 sink.write(CONNECTION_PREFACE.toByteArray()); 402 sink.flush(); 403 } 404 synStream(boolean outFinished, boolean inFinished, int streamId, int associatedStreamId, List<Header> headerBlock)405 @Override public synchronized void synStream(boolean outFinished, boolean inFinished, 406 int streamId, int associatedStreamId, List<Header> headerBlock) 407 throws IOException { 408 if (inFinished) throw new UnsupportedOperationException(); 409 if (closed) throw new IOException("closed"); 410 headers(outFinished, streamId, headerBlock); 411 } 412 synReply(boolean outFinished, int streamId, List<Header> headerBlock)413 @Override public synchronized void synReply(boolean outFinished, int streamId, 414 List<Header> headerBlock) throws IOException { 415 if (closed) throw new IOException("closed"); 416 headers(outFinished, streamId, headerBlock); 417 } 418 headers(int streamId, List<Header> headerBlock)419 @Override public synchronized void headers(int streamId, List<Header> headerBlock) 420 throws IOException { 421 if (closed) throw new IOException("closed"); 422 headers(false, streamId, headerBlock); 423 } 424 pushPromise(int streamId, int promisedStreamId, List<Header> requestHeaders)425 @Override public synchronized void pushPromise(int streamId, int promisedStreamId, 426 List<Header> requestHeaders) throws IOException { 427 if (closed) throw new IOException("closed"); 428 if (hpackBuffer.size() != 0) throw new IllegalStateException(); 429 hpackWriter.writeHeaders(requestHeaders); 430 431 long byteCount = hpackBuffer.size(); 432 int length = (int) Math.min(maxFrameSize - 4, byteCount); 433 byte type = TYPE_PUSH_PROMISE; 434 byte flags = byteCount == length ? FLAG_END_HEADERS : 0; 435 frameHeader(streamId, length + 4, type, flags); 436 sink.writeInt(promisedStreamId & 0x7fffffff); 437 sink.write(hpackBuffer, length); 438 439 if (byteCount > length) writeContinuationFrames(streamId, byteCount - length); 440 } 441 headers(boolean outFinished, int streamId, List<Header> headerBlock)442 void headers(boolean outFinished, int streamId, List<Header> headerBlock) throws IOException { 443 if (closed) throw new IOException("closed"); 444 if (hpackBuffer.size() != 0) throw new IllegalStateException(); 445 hpackWriter.writeHeaders(headerBlock); 446 447 long byteCount = hpackBuffer.size(); 448 int length = (int) Math.min(maxFrameSize, byteCount); 449 byte type = TYPE_HEADERS; 450 byte flags = byteCount == length ? FLAG_END_HEADERS : 0; 451 if (outFinished) flags |= FLAG_END_STREAM; 452 frameHeader(streamId, length, type, flags); 453 sink.write(hpackBuffer, length); 454 455 if (byteCount > length) writeContinuationFrames(streamId, byteCount - length); 456 } 457 writeContinuationFrames(int streamId, long byteCount)458 private void writeContinuationFrames(int streamId, long byteCount) throws IOException { 459 while (byteCount > 0) { 460 int length = (int) Math.min(maxFrameSize, byteCount); 461 byteCount -= length; 462 frameHeader(streamId, length, TYPE_CONTINUATION, byteCount == 0 ? FLAG_END_HEADERS : 0); 463 sink.write(hpackBuffer, length); 464 } 465 } 466 rstStream(int streamId, ErrorCode errorCode)467 @Override public synchronized void rstStream(int streamId, ErrorCode errorCode) 468 throws IOException { 469 if (closed) throw new IOException("closed"); 470 if (errorCode.spdyRstCode == -1) throw new IllegalArgumentException(); 471 472 int length = 4; 473 byte type = TYPE_RST_STREAM; 474 byte flags = FLAG_NONE; 475 frameHeader(streamId, length, type, flags); 476 sink.writeInt(errorCode.httpCode); 477 sink.flush(); 478 } 479 maxDataLength()480 @Override public int maxDataLength() { 481 return maxFrameSize; 482 } 483 data(boolean outFinished, int streamId, Buffer source, int byteCount)484 @Override public synchronized void data(boolean outFinished, int streamId, Buffer source, 485 int byteCount) throws IOException { 486 if (closed) throw new IOException("closed"); 487 byte flags = FLAG_NONE; 488 if (outFinished) flags |= FLAG_END_STREAM; 489 dataFrame(streamId, flags, source, byteCount); 490 } 491 dataFrame(int streamId, byte flags, Buffer buffer, int byteCount)492 void dataFrame(int streamId, byte flags, Buffer buffer, int byteCount) throws IOException { 493 byte type = TYPE_DATA; 494 frameHeader(streamId, byteCount, type, flags); 495 if (byteCount > 0) { 496 sink.write(buffer, byteCount); 497 } 498 } 499 settings(Settings settings)500 @Override public synchronized void settings(Settings settings) throws IOException { 501 if (closed) throw new IOException("closed"); 502 int length = settings.size() * 6; 503 byte type = TYPE_SETTINGS; 504 byte flags = FLAG_NONE; 505 int streamId = 0; 506 frameHeader(streamId, length, type, flags); 507 for (int i = 0; i < Settings.COUNT; i++) { 508 if (!settings.isSet(i)) continue; 509 int id = i; 510 if (id == 4) id = 3; // SETTINGS_MAX_CONCURRENT_STREAMS renumbered. 511 else if (id == 7) id = 4; // SETTINGS_INITIAL_WINDOW_SIZE renumbered. 512 sink.writeShort(id); 513 sink.writeInt(settings.get(i)); 514 } 515 sink.flush(); 516 } 517 ping(boolean ack, int payload1, int payload2)518 @Override public synchronized void ping(boolean ack, int payload1, int payload2) 519 throws IOException { 520 if (closed) throw new IOException("closed"); 521 int length = 8; 522 byte type = TYPE_PING; 523 byte flags = ack ? FLAG_ACK : FLAG_NONE; 524 int streamId = 0; 525 frameHeader(streamId, length, type, flags); 526 sink.writeInt(payload1); 527 sink.writeInt(payload2); 528 sink.flush(); 529 } 530 goAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData)531 @Override public synchronized void goAway(int lastGoodStreamId, ErrorCode errorCode, 532 byte[] debugData) throws IOException { 533 if (closed) throw new IOException("closed"); 534 if (errorCode.httpCode == -1) throw illegalArgument("errorCode.httpCode == -1"); 535 int length = 8 + debugData.length; 536 byte type = TYPE_GOAWAY; 537 byte flags = FLAG_NONE; 538 int streamId = 0; 539 frameHeader(streamId, length, type, flags); 540 sink.writeInt(lastGoodStreamId); 541 sink.writeInt(errorCode.httpCode); 542 if (debugData.length > 0) { 543 sink.write(debugData); 544 } 545 sink.flush(); 546 } 547 windowUpdate(int streamId, long windowSizeIncrement)548 @Override public synchronized void windowUpdate(int streamId, long windowSizeIncrement) 549 throws IOException { 550 if (closed) throw new IOException("closed"); 551 if (windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL) { 552 throw illegalArgument("windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL: %s", 553 windowSizeIncrement); 554 } 555 int length = 4; 556 byte type = TYPE_WINDOW_UPDATE; 557 byte flags = FLAG_NONE; 558 frameHeader(streamId, length, type, flags); 559 sink.writeInt((int) windowSizeIncrement); 560 sink.flush(); 561 } 562 close()563 @Override public synchronized void close() throws IOException { 564 closed = true; 565 sink.close(); 566 } 567 frameHeader(int streamId, int length, byte type, byte flags)568 void frameHeader(int streamId, int length, byte type, byte flags) throws IOException { 569 if (logger.isLoggable(FINE)) logger.fine(formatHeader(false, streamId, length, type, flags)); 570 if (length > maxFrameSize) { 571 throw illegalArgument("FRAME_SIZE_ERROR length > %d: %d", maxFrameSize, length); 572 } 573 if ((streamId & 0x80000000) != 0) throw illegalArgument("reserved bit set: %s", streamId); 574 writeMedium(sink, length); 575 sink.writeByte(type & 0xff); 576 sink.writeByte(flags & 0xff); 577 sink.writeInt(streamId & 0x7fffffff); 578 } 579 } 580 illegalArgument(String message, Object... args)581 private static IllegalArgumentException illegalArgument(String message, Object... args) { 582 throw new IllegalArgumentException(format(message, args)); 583 } 584 ioException(String message, Object... args)585 private static IOException ioException(String message, Object... args) throws IOException { 586 throw new IOException(format(message, args)); 587 } 588 589 /** 590 * Decompression of the header block occurs above the framing layer. This 591 * class lazily reads continuation frames as they are needed by {@link 592 * Hpack.Reader#readHeaders()}. 593 */ 594 static final class ContinuationSource implements Source { 595 private final BufferedSource source; 596 597 int length; 598 byte flags; 599 int streamId; 600 601 int left; 602 short padding; 603 ContinuationSource(BufferedSource source)604 public ContinuationSource(BufferedSource source) { 605 this.source = source; 606 } 607 read(Buffer sink, long byteCount)608 @Override public long read(Buffer sink, long byteCount) throws IOException { 609 while (left == 0) { 610 source.skip(padding); 611 padding = 0; 612 if ((flags & FLAG_END_HEADERS) != 0) return -1; 613 readContinuationHeader(); 614 // TODO: test case for empty continuation header? 615 } 616 617 long read = source.read(sink, Math.min(byteCount, left)); 618 if (read == -1) return -1; 619 left -= read; 620 return read; 621 } 622 timeout()623 @Override public Timeout timeout() { 624 return source.timeout(); 625 } 626 close()627 @Override public void close() throws IOException { 628 } 629 readContinuationHeader()630 private void readContinuationHeader() throws IOException { 631 int previousStreamId = streamId; 632 633 length = left = readMedium(source); 634 byte type = (byte) (source.readByte() & 0xff); 635 flags = (byte) (source.readByte() & 0xff); 636 if (logger.isLoggable(FINE)) logger.fine(formatHeader(true, streamId, length, type, flags)); 637 streamId = (source.readInt() & 0x7fffffff); 638 if (type != TYPE_CONTINUATION) throw ioException("%s != TYPE_CONTINUATION", type); 639 if (streamId != previousStreamId) throw ioException("TYPE_CONTINUATION streamId changed"); 640 } 641 } 642 lengthWithoutPadding(int length, byte flags, short padding)643 private static int lengthWithoutPadding(int length, byte flags, short padding) 644 throws IOException { 645 if ((flags & FLAG_PADDED) != 0) length--; // Account for reading the padding length. 646 if (padding > length) { 647 throw ioException("PROTOCOL_ERROR padding %s > remaining length %s", padding, length); 648 } 649 return (short) (length - padding); 650 } 651 652 /** 653 * Logs a human-readable representation of HTTP/2 frame headers. 654 * 655 * <p>The format is: 656 * 657 * <pre> 658 * direction streamID length type flags 659 * </pre> 660 * Where direction is {@code <<} for inbound and {@code >>} for outbound. 661 * 662 * <p> For example, the following would indicate a HEAD request sent from 663 * the client. 664 * <pre> 665 * {@code 666 * << 0x0000000f 12 HEADERS END_HEADERS|END_STREAM 667 * } 668 * </pre> 669 */ 670 static final class FrameLogger { 671 formatHeader(boolean inbound, int streamId, int length, byte type, byte flags)672 static String formatHeader(boolean inbound, int streamId, int length, byte type, byte flags) { 673 String formattedType = type < TYPES.length ? TYPES[type] : format("0x%02x", type); 674 String formattedFlags = formatFlags(type, flags); 675 return format("%s 0x%08x %5d %-13s %s", inbound ? "<<" : ">>", streamId, length, 676 formattedType, formattedFlags); 677 } 678 679 /** 680 * Looks up valid string representing flags from the table. Invalid 681 * combinations are represented in binary. 682 */ 683 // Visible for testing. 684 static String formatFlags(byte type, byte flags) { 685 if (flags == 0) return ""; 686 switch (type) { // Special case types that have 0 or 1 flag. 687 case TYPE_SETTINGS: 688 case TYPE_PING: 689 return flags == FLAG_ACK ? "ACK" : BINARY[flags]; 690 case TYPE_PRIORITY: 691 case TYPE_RST_STREAM: 692 case TYPE_GOAWAY: 693 case TYPE_WINDOW_UPDATE: 694 return BINARY[flags]; 695 } 696 String result = flags < FLAGS.length ? FLAGS[flags] : BINARY[flags]; 697 // Special case types that have overlap flag values. 698 if (type == TYPE_PUSH_PROMISE && (flags & FLAG_END_PUSH_PROMISE) != 0) { 699 return result.replace("HEADERS", "PUSH_PROMISE"); // TODO: Avoid allocation. 700 } else if (type == TYPE_DATA && (flags & FLAG_COMPRESSED) != 0) { 701 return result.replace("PRIORITY", "COMPRESSED"); // TODO: Avoid allocation. 702 } 703 return result; 704 } 705 706 /** Lookup table for valid frame types. */ 707 private static final String[] TYPES = new String[] { 708 "DATA", 709 "HEADERS", 710 "PRIORITY", 711 "RST_STREAM", 712 "SETTINGS", 713 "PUSH_PROMISE", 714 "PING", 715 "GOAWAY", 716 "WINDOW_UPDATE", 717 "CONTINUATION" 718 }; 719 720 /** 721 * Lookup table for valid flags for DATA, HEADERS, CONTINUATION. Invalid 722 * combinations are represented in binary. 723 */ 724 private static final String[] FLAGS = new String[0x40]; // Highest bit flag is 0x20. 725 private static final String[] BINARY = new String[256]; 726 727 static { 728 for (int i = 0; i < BINARY.length; i++) { 729 BINARY[i] = format("%8s", Integer.toBinaryString(i)).replace(' ', '0'); 730 } 731 732 FLAGS[FLAG_NONE] = ""; 733 FLAGS[FLAG_END_STREAM] = "END_STREAM"; 734 735 int[] prefixFlags = new int[] {FLAG_END_STREAM}; 736 737 FLAGS[FLAG_PADDED] = "PADDED"; 738 for (int prefixFlag : prefixFlags) { 739 FLAGS[prefixFlag | FLAG_PADDED] = FLAGS[prefixFlag] + "|PADDED"; 740 } 741 742 FLAGS[FLAG_END_HEADERS] = "END_HEADERS"; // Same as END_PUSH_PROMISE. 743 FLAGS[FLAG_PRIORITY] = "PRIORITY"; // Same as FLAG_COMPRESSED. 744 FLAGS[FLAG_END_HEADERS | FLAG_PRIORITY] = "END_HEADERS|PRIORITY"; // Only valid on HEADERS. 745 int[] frameFlags = 746 new int[] {FLAG_END_HEADERS, FLAG_PRIORITY, FLAG_END_HEADERS | FLAG_PRIORITY}; 747 748 for (int frameFlag : frameFlags) { 749 for (int prefixFlag : prefixFlags) { 750 FLAGS[prefixFlag | frameFlag] = FLAGS[prefixFlag] + '|' + FLAGS[frameFlag]; 751 FLAGS[prefixFlag | frameFlag | FLAG_PADDED] = 752 FLAGS[prefixFlag] + '|' + FLAGS[frameFlag] + "|PADDED"; 753 } 754 } 755 756 for (int i = 0; i < FLAGS.length; i++) { // Fill in holes with binary representation. 757 if (FLAGS[i] == null) FLAGS[i] = BINARY[i]; 758 } 759 } 760 } 761 762 private static int readMedium(BufferedSource source) throws IOException { 763 return (source.readByte() & 0xff) << 16 764 | (source.readByte() & 0xff) << 8 765 | (source.readByte() & 0xff); 766 } 767 768 private static void writeMedium(BufferedSink sink, int i) throws IOException { 769 sink.writeByte((i >>> 16) & 0xff); 770 sink.writeByte((i >>> 8) & 0xff); 771 sink.writeByte(i & 0xff); 772 } 773 } 774