void receiveHeaders(List<String> headers, HeadersMode headersMode) {
   assert (!Thread.holdsLock(SpdyStream.this));
   ErrorCode errorCode = null;
   boolean open = true;
   synchronized (this) {
     if (responseHeaders == null) {
       if (headersMode.failIfHeadersAbsent()) {
         errorCode = ErrorCode.PROTOCOL_ERROR;
       } else {
         responseHeaders = headers;
         open = isOpen();
         notifyAll();
       }
     } else {
       if (headersMode.failIfHeadersPresent()) {
         errorCode = ErrorCode.STREAM_IN_USE;
       } else {
         List<String> newHeaders = new ArrayList<String>();
         newHeaders.addAll(responseHeaders);
         newHeaders.addAll(headers);
         this.responseHeaders = newHeaders;
       }
     }
   }
   if (errorCode != null) {
     closeLater(errorCode);
   } else if (!open) {
     connection.removeStream(id);
   }
 }
 void receiveFin() {
   assert (!Thread.holdsLock(SpdyStream.this));
   boolean open;
   synchronized (this) {
     this.in.finished = true;
     open = isOpen();
     notifyAll();
   }
   if (!open) {
     connection.removeStream(id);
   }
 }
 /** Returns true if this stream was closed. */
 private boolean closeInternal(ErrorCode errorCode) {
   assert (!Thread.holdsLock(this));
   synchronized (this) {
     if (this.errorCode != null) {
       return false;
     }
     if (in.finished && out.finished) {
       return false;
     }
     this.errorCode = errorCode;
     notifyAll();
   }
   connection.removeStream(id);
   return true;
 }
Пример #4
0
  /** Create an {@code SSLSocket} and perform the TLS handshake and certificate validation. */
  private void upgradeToTls(TunnelRequest tunnelRequest) throws IOException {
    Platform platform = Platform.get();

    // Make an SSL Tunnel on the first message pair of each SSL + proxy connection.
    if (requiresTunnel()) {
      makeTunnel(tunnelRequest);
    }

    // Create the wrapper over connected socket.
    socket =
        route.address.sslSocketFactory.createSocket(
            socket, route.address.uriHost, route.address.uriPort, true /* autoClose */);
    SSLSocket sslSocket = (SSLSocket) socket;
    if (route.modernTls) {
      platform.enableTlsExtensions(sslSocket, route.address.uriHost);
    } else {
      platform.supportTlsIntolerantServer(sslSocket);
    }

    boolean useNpn = route.modernTls && route.address.transports.contains("spdy/3");
    if (useNpn) {
      platform.setNpnProtocols(sslSocket, NPN_PROTOCOLS);
    }

    // Force handshake. This can throw!
    sslSocket.startHandshake();

    // Verify that the socket's certificates are acceptable for the target host.
    if (!route.address.hostnameVerifier.verify(route.address.uriHost, sslSocket.getSession())) {
      throw new IOException("Hostname '" + route.address.uriHost + "' was not verified");
    }

    out = sslSocket.getOutputStream();
    in = sslSocket.getInputStream();

    byte[] selectedProtocol;
    if (useNpn && (selectedProtocol = platform.getNpnSelectedProtocol(sslSocket)) != null) {
      if (Arrays.equals(selectedProtocol, SPDY3)) {
        sslSocket.setSoTimeout(0); // SPDY timeouts are set per-stream.
        spdyConnection =
            new SpdyConnection.Builder(route.address.getUriHost(), true, in, out).build();
        spdyConnection.sendConnectionHeader();
      } else if (!Arrays.equals(selectedProtocol, HTTP_11)) {
        throw new IOException(
            "Unexpected NPN transport " + new String(selectedProtocol, "ISO-8859-1"));
      }
    }
  }
 private void cancelStreamIfNecessary() throws IOException {
   assert (!Thread.holdsLock(SpdyStream.this));
   boolean open;
   boolean cancel;
   synchronized (this) {
     cancel = !in.finished && in.closed && (out.finished || out.closed);
     open = isOpen();
   }
   if (cancel) {
     // RST this stream to prevent additional data from being sent. This
     // is safe because the input stream is closed (we won't use any
     // further bytes) and the output stream is either finished or closed
     // (so RSTing both streams doesn't cause harm).
     SpdyStream.this.close(ErrorCode.CANCEL);
   } else if (!open) {
     connection.removeStream(id);
   }
 }
 /**
  * Sends a reply to an incoming stream.
  *
  * @param out true to create an output stream that we can use to send data to the remote peer.
  *     Corresponds to {@code FLAG_FIN}.
  */
 public void reply(List<String> responseHeaders, boolean out) throws IOException {
   assert (!Thread.holdsLock(SpdyStream.this));
   boolean outFinished = false;
   synchronized (this) {
     if (responseHeaders == null) {
       throw new NullPointerException("responseHeaders == null");
     }
     if (isLocallyInitiated()) {
       throw new IllegalStateException("cannot reply to a locally initiated stream");
     }
     if (this.responseHeaders != null) {
       throw new IllegalStateException("reply already sent");
     }
     this.responseHeaders = responseHeaders;
     if (!out) {
       this.out.finished = true;
       outFinished = true;
     }
   }
   connection.writeSynReply(id, outFinished, responseHeaders);
 }
Пример #7
0
 /**
  * Returns the time in ns when this connection became idle. Undefined if this connection is not
  * idle.
  */
 public long getIdleStartTimeNs() {
   return spdyConnection == null ? idleStartTimeNs : spdyConnection.getIdleStartTimeNs();
 }
Пример #8
0
 /** Returns true if this connection is idle. */
 public boolean isIdle() {
   return spdyConnection == null || spdyConnection.isIdle();
 }
Пример #9
0
  /** Create an {@code SSLSocket} and perform the TLS handshake and certificate validation. */
  private void upgradeToTls(TunnelRequest tunnelRequest) throws IOException {
    Platform platform = Platform.get();

    // Make an SSL Tunnel on the first message pair of each SSL + proxy connection.
    if (requiresTunnel()) {
      makeTunnel(tunnelRequest);
    }

    // Create the wrapper over connected socket.
    socket =
        route.address.sslSocketFactory.createSocket(
            socket, route.address.uriHost, route.address.uriPort, true /* autoClose */);
    SSLSocket sslSocket = (SSLSocket) socket;
    if (route.modernTls) {
      platform.enableTlsExtensions(sslSocket, route.address.uriHost);
    } else {
      platform.supportTlsIntolerantServer(sslSocket);
    }

    boolean useNpn =
        route.modernTls
            && ( // Contains a spdy variant.
            route.address.protocols.contains(Protocol.HTTP_2)
                || route.address.protocols.contains(Protocol.SPDY_3));

    if (useNpn) {
      if (route.address.protocols.contains(Protocol.HTTP_2) // Contains both spdy variants.
          && route.address.protocols.contains(Protocol.SPDY_3)) {
        platform.setNpnProtocols(sslSocket, Protocol.HTTP2_SPDY3_AND_HTTP);
      } else if (route.address.protocols.contains(Protocol.HTTP_2)) {
        platform.setNpnProtocols(sslSocket, Protocol.HTTP2_AND_HTTP_11);
      } else {
        platform.setNpnProtocols(sslSocket, Protocol.SPDY3_AND_HTTP11);
      }
    }

    // Force handshake. This can throw!
    sslSocket.startHandshake();

    // Verify that the socket's certificates are acceptable for the target host.
    if (!route.address.hostnameVerifier.verify(route.address.uriHost, sslSocket.getSession())) {
      throw new IOException("Hostname '" + route.address.uriHost + "' was not verified");
    }

    out = sslSocket.getOutputStream();
    in = sslSocket.getInputStream();
    handshake = Handshake.get(sslSocket.getSession());
    streamWrapper();

    ByteString maybeProtocol;
    if (useNpn && (maybeProtocol = platform.getNpnSelectedProtocol(sslSocket)) != null) {
      Protocol selectedProtocol = Protocol.find(maybeProtocol); // Throws IOE on unknown.
      if (selectedProtocol.spdyVariant) {
        sslSocket.setSoTimeout(0); // SPDY timeouts are set per-stream.
        spdyConnection =
            new SpdyConnection.Builder(route.address.getUriHost(), true, in, out)
                .protocol(selectedProtocol)
                .build();
        spdyConnection.sendConnectionHeader();
      }
    }
  }
 /**
  * Abnormally terminate this stream. This enqueues a {@code RST_STREAM} frame and returns
  * immediately.
  */
 public void closeLater(ErrorCode errorCode) {
   if (!closeInternal(errorCode)) {
     return; // Already closed.
   }
   connection.writeSynResetLater(id, errorCode);
 }
 /**
  * Abnormally terminate this stream. This blocks until the {@code RST_STREAM} frame has been
  * transmitted.
  */
 public void close(ErrorCode rstStatusCode) throws IOException {
   if (!closeInternal(rstStatusCode)) {
     return; // Already closed.
   }
   connection.writeSynReset(id, rstStatusCode);
 }