Пример #1
0
  /**
   * Callers of this method are not thread safe, and sometimes on application threads. Most often,
   * this method will be called to send a buffer worth of data to the peer.
   *
   * <p>Writes are subject to the write window of the stream and the connection. Until there is a
   * window sufficient to send {@code byteCount}, the caller will block. For example, a user of
   * {@code HttpURLConnection} who flushes more bytes to the output stream than the connection's
   * write window will block.
   *
   * <p>Zero {@code byteCount} writes are not subject to flow control and will not block. The only
   * use case for zero {@code byteCount} is closing a flushed output stream.
   */
  public void writeData(int streamId, boolean outFinished, Buffer buffer, long byteCount)
      throws IOException {
    if (byteCount == 0) { // Empty data frames are not flow-controlled.
      frameWriter.data(outFinished, streamId, buffer, 0);
      return;
    }

    while (byteCount > 0) {
      int toWrite;
      synchronized (FramedConnection.this) {
        try {
          while (bytesLeftInWriteWindow <= 0) {
            // Before blocking, confirm that the stream we're writing is still open. It's possible
            // that the stream has since been closed (such as if this write timed out.)
            if (!streams.containsKey(streamId)) {
              throw new IOException("stream closed");
            }
            FramedConnection.this.wait(); // Wait until we receive a WINDOW_UPDATE.
          }
        } catch (InterruptedException e) {
          throw new InterruptedIOException();
        }

        toWrite = (int) Math.min(byteCount, bytesLeftInWriteWindow);
        toWrite = Math.min(toWrite, frameWriter.maxDataLength());
        bytesLeftInWriteWindow -= toWrite;
      }

      byteCount -= toWrite;
      frameWriter.data(outFinished && byteCount == 0, streamId, buffer, toWrite);
    }
  }
  /**
   * Callers of this method are not thread safe, and sometimes on application threads. Most often,
   * this method will be called to send a buffer worth of data to the peer.
   *
   * <p>Writes are subject to the write window of the stream and the connection. Until there is a
   * window sufficient to send {@code byteCount}, the caller will block. For example, a user of
   * {@code HttpURLConnection} who flushes more bytes to the output stream than the connection's
   * write window will block.
   *
   * <p>Zero {@code byteCount} writes are not subject to flow control and will not block. The only
   * use case for zero {@code byteCount} is closing a flushed output stream.
   */
  public void writeData(int streamId, boolean outFinished, OkBuffer buffer, long byteCount)
      throws IOException {
    if (byteCount == 0) { // Empty data frames are not flow-controlled.
      frameWriter.data(outFinished, streamId, buffer, 0);
      return;
    }

    while (byteCount > 0) {
      int toWrite;
      synchronized (SpdyConnection.this) {
        try {
          while (bytesLeftInWriteWindow <= 0) {
            SpdyConnection.this.wait(); // Wait until we receive a WINDOW_UPDATE.
          }
        } catch (InterruptedException e) {
          throw new InterruptedIOException();
        }

        toWrite = (int) Math.min(Math.min(byteCount, bytesLeftInWriteWindow), maxFrameSize);
        bytesLeftInWriteWindow -= toWrite;
      }

      byteCount -= toWrite;
      frameWriter.data(outFinished && byteCount == 0, streamId, buffer, toWrite);
    }
  }
Пример #3
0
 /**
  * @param sendConnectionPreface true to send connection preface frames. This should always be true
  *     except for in tests that don't check for a connection preface.
  */
 void start(boolean sendConnectionPreface) throws IOException {
   if (sendConnectionPreface) {
     frameWriter.connectionPreface();
     frameWriter.settings(okHttpSettings);
     int windowSize = okHttpSettings.getInitialWindowSize(Settings.DEFAULT_INITIAL_WINDOW_SIZE);
     if (windowSize != Settings.DEFAULT_INITIAL_WINDOW_SIZE) {
       frameWriter.windowUpdate(0, windowSize - Settings.DEFAULT_INITIAL_WINDOW_SIZE);
     }
   }
   new Thread(readerRunnable).start(); // Not a daemon thread.
 }
 private void writePing(boolean reply, int payload1, int payload2, Ping ping) throws IOException {
   synchronized (frameWriter) {
     // Observe the sent time immediately before performing I/O.
     if (ping != null) ping.send();
     frameWriter.ping(reply, payload1, payload2);
   }
 }
    /**
     * Called to perform a window update for this stream (or connection). Updates the window size
     * back to the size of the initial window and sends a window update frame to the remote
     * endpoint.
     */
    void updateWindow(FrameWriter frameWriter) throws Http2Exception {
      // Expand the window for this stream back to the size of the initial window.
      int deltaWindowSize = initialWindowSize - windowSize();
      addAndGet(deltaWindowSize);

      // Send a window update for the stream/connection.
      frameWriter.writeFrame(streamId, deltaWindowSize);
    }
Пример #6
0
  private void close(ErrorCode connectionCode, ErrorCode streamCode) throws IOException {
    assert (!Thread.holdsLock(this));
    IOException thrown = null;
    try {
      shutdown(connectionCode);
    } catch (IOException e) {
      thrown = e;
    }

    FramedStream[] streamsToClose = null;
    Ping[] pingsToCancel = null;
    synchronized (this) {
      if (!streams.isEmpty()) {
        streamsToClose = streams.values().toArray(new FramedStream[streams.size()]);
        streams.clear();
        setIdle(false);
      }
      if (pings != null) {
        pingsToCancel = pings.values().toArray(new Ping[pings.size()]);
        pings = null;
      }
    }

    if (streamsToClose != null) {
      for (FramedStream stream : streamsToClose) {
        try {
          stream.close(streamCode);
        } catch (IOException e) {
          if (thrown != null) thrown = e;
        }
      }
    }

    if (pingsToCancel != null) {
      for (Ping ping : pingsToCancel) {
        ping.cancel();
      }
    }

    // Close the writer to release its resources (such as deflaters).
    try {
      frameWriter.close();
    } catch (IOException e) {
      if (thrown == null) thrown = e;
    }

    // Close the socket to break out the reader thread, which will clean up after itself.
    try {
      socket.close();
    } catch (IOException e) {
      thrown = e;
    }

    if (thrown != null) throw thrown;
  }
Пример #7
0
 /** Merges {@code settings} into this peer's settings and sends them to the remote peer. */
 public void setSettings(Settings settings) throws IOException {
   synchronized (frameWriter) {
     synchronized (this) {
       if (shutdown) {
         throw new IOException("shutdown");
       }
       okHttpSettings.merge(settings);
       frameWriter.settings(settings);
     }
   }
 }
  private void close(ErrorCode connectionCode, ErrorCode streamCode) throws IOException {
    assert (!Thread.holdsLock(this));
    IOException thrown = null;
    try {
      shutdown(connectionCode);
    } catch (IOException e) {
      thrown = e;
    }

    SpdyStream[] streamsToClose = null;
    Ping[] pingsToCancel = null;
    synchronized (this) {
      if (!streams.isEmpty()) {
        streamsToClose = streams.values().toArray(new SpdyStream[streams.size()]);
        streams.clear();
        setIdle(false);
      }
      if (pings != null) {
        pingsToCancel = pings.values().toArray(new Ping[pings.size()]);
        pings = null;
      }
    }

    if (streamsToClose != null) {
      for (SpdyStream stream : streamsToClose) {
        try {
          stream.close(streamCode);
        } catch (IOException e) {
          if (thrown != null) thrown = e;
        }
      }
    }

    if (pingsToCancel != null) {
      for (Ping ping : pingsToCancel) {
        ping.cancel();
      }
    }

    try {
      frameReader.close();
    } catch (IOException e) {
      thrown = e;
    }
    try {
      frameWriter.close();
    } catch (IOException e) {
      if (thrown == null) thrown = e;
    }

    if (thrown != null) throw thrown;
  }
  private SpdyStream newStream(
      int associatedStreamId, List<Header> requestHeaders, boolean out, boolean in)
      throws IOException {
    boolean outFinished = !out;
    boolean inFinished = !in;
    int priority = -1; // TODO: permit the caller to specify a priority?
    int slot = 0; // TODO: permit the caller to specify a slot?
    SpdyStream stream;
    int streamId;

    synchronized (frameWriter) {
      synchronized (this) {
        if (shutdown) {
          throw new IOException("shutdown");
        }
        streamId = nextStreamId;
        nextStreamId += 2;
        stream = new SpdyStream(streamId, this, outFinished, inFinished, priority, requestHeaders);
        if (stream.isOpen()) {
          streams.put(streamId, stream);
          setIdle(false);
        }
      }
      if (associatedStreamId == 0) {
        frameWriter.synStream(
            outFinished, inFinished, streamId, associatedStreamId, priority, slot, requestHeaders);
      } else if (client) {
        throw new IllegalArgumentException("client streams shouldn't have associated stream IDs");
      } else { // HTTP/2 has a PUSH_PROMISE frame.
        frameWriter.pushPromise(associatedStreamId, streamId, requestHeaders);
      }
    }

    if (!out) {
      frameWriter.flush();
    }

    return stream;
  }
    /**
     * Called to perform a window update for this stream (or connection). Updates the window size
     * back to the size of the initial window and sends a window update frame to the remote
     * endpoint.
     */
    public void updateWindow(FrameWriter frameWriter) throws Http2Exception {
      // Expand the window for this stream back to the size of the initial window.
      int deltaWindowSize = initialWindowSize - getSize();
      addAndGet(deltaWindowSize);

      // Send a window update for the stream/connection.
      Http2WindowUpdateFrame updateFrame =
          new DefaultHttp2WindowUpdateFrame.Builder()
              .setStreamId(streamId)
              .setWindowSizeIncrement(deltaWindowSize)
              .build();
      frameWriter.writeFrame(updateFrame);
    }
 /**
  * Degrades this connection such that new streams can neither be created locally, nor accepted
  * from the remote peer. Existing streams are not impacted. This is intended to permit an endpoint
  * to gracefully stop accepting new requests without harming previously established streams.
  */
 public void shutdown(ErrorCode statusCode) throws IOException {
   synchronized (frameWriter) {
     int lastGoodStreamId;
     synchronized (this) {
       if (shutdown) {
         return;
       }
       shutdown = true;
       lastGoodStreamId = this.lastGoodStreamId;
     }
     // TODO: propagate exception message into debugData
     frameWriter.goAway(lastGoodStreamId, statusCode, Util.EMPTY_BYTE_ARRAY);
   }
 }
 /**
  * Sends a connection header if the current variant requires it. This should be called after
  * {@link Builder#build} for all new connections.
  */
 public void sendConnectionHeader() throws IOException {
   frameWriter.connectionHeader();
   frameWriter.settings(okHttpSettings);
 }
 public void flush() throws IOException {
   frameWriter.flush();
 }
 void writeSynReset(int streamId, ErrorCode statusCode) throws IOException {
   frameWriter.rstStream(streamId, statusCode);
 }
 void writeSynReply(int streamId, boolean outFinished, List<Header> alternating)
     throws IOException {
   frameWriter.synReply(outFinished, streamId, alternating);
 }