Skip to content

Instantly share code, notes, and snippets.

@wsky
Last active August 29, 2015 14:12
Show Gist options
  • Save wsky/597c433e28d62f8e5998 to your computer and use it in GitHub Desktop.
Save wsky/597c433e28d62f8e5998 to your computer and use it in GitHub Desktop.
SPDY support
  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
 
  <!-- ============================================================= -->
  <!-- Configure the Jetty Server instance with an ID "Server"       -->
  <!-- by adding a SPDY connector.                                   -->
  <!-- This configuration must be used in conjunction with jetty.xml -->
  <!-- It should not be used with jetty-https.xml as this connector  -->
  <!-- can provide both HTTPS and SPDY connections                   -->
  <!-- ============================================================= -->
  <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
    <!-- =========================================================== -->
    <!-- Set up the SSL Context factory used to establish all TLS     -->
    <!-- Connections and session.                                    -->
    <!--                                                             -->
    <!-- Consult the javadoc of o.e.j.util.ssl.SslContextFactory     -->
    <!-- o.e.j.server.HttpConnectionFactory for all configuration    -->
    <!-- that may be set here.                                       -->
    <!-- =========================================================== -->
    <New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
      <Set name="KeyStorePath"><Property name="jetty.home" default="." />/etc/keystore</Set>
      <Set name="KeyStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
      <Set name="KeyManagerPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
      <Set name="TrustStorePath"><Property name="jetty.home" default="." />/etc/keystore</Set>
      <Set name="TrustStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
    </New>
 
    <!-- =========================================================== -->
    <!-- Enables NPN debugging on System.err                         -->
    <!-- ===========================================================
    <Set class="org.eclipse.jetty.npn.NextProtoNego" name="debug" type="boolean">true</Set>
    -->
 
    <!-- =========================================================== -->
    <!-- Create a TLS specific HttpConfiguration based on the        -->
    <!-- common HttpConfiguration defined in jetty.xml               -->
    <!-- Add a SecureRequestCustomizer to extract certificate and    -->
    <!-- session information                                         -->
    <!-- =========================================================== -->
    <New id="tlsHttpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
      <Arg><Ref refid="httpConfig"/></Arg>
      <Call name="addCustomizer">
        <Arg><New class="org.eclipse.jetty.server.SecureRequestCustomizer"/></Arg>
      </Call>
    </New>
 
    <!-- =========================================================== -->
    <!-- This is the upstream server connector.                      -->
    <!-- It speaks HTTP on port 9090.                                -->
    <!-- =========================================================== -->
    <Call name="addConnector">
      <Arg>
        <New class="org.eclipse.jetty.server.ServerConnector">
          <Arg name="server"><Ref id="Server" /></Arg>
          <Arg name="factories">
            <Array type="org.eclipse.jetty.server.ConnectionFactory">
              <Item>
                <New class="org.eclipse.jetty.server.HttpConnectionFactory">
                  <Arg name="config"><Ref id="httpConfig" /></Arg>
                </New>
              </Item>
            </Array>
          </Arg>
          <Set name="host"><Property name="jetty.host" /></Set>
          <Set name="port"><Property name="jetty.port" default="9090" /></Set>
          <Set name="idleTimeout">30000</Set>
        </New>
      </Arg>
    </Call>
 
  <!-- =========================================================== -->
  <!-- This ProxyEngine translates the incoming SPDY/x(HTTP)       -->
  <!-- requests to HTTP                                            -->
  <!-- =========================================================== -->
  <New id="httpProxyEngine" class="org.eclipse.jetty.spdy.server.proxy.HTTPProxyEngine">
    <Arg>
      <New class="org.eclipse.jetty.client.HttpClient">
        <Call name="start"/>
      </New>
    </Arg>
  </New>
 
  <!-- =========================================================== -->
  <!-- The ProxyEngineSelector receives SPDY/x(HTTP) requests      -->
  <!-- from proxy connectors below and is configured to process    -->
  <!-- requests for host "localhost".                              -->
  <!-- Such requests are converted from SPDY/x(HTTP) to            -->
  <!-- HTTP by the configured ProxyEngine and forwarded            -->
  <!-- to 127.0.0.1:9090, where they are served by the upstream    -->
  <!-- server above.                                               -->
  <!-- =========================================================== -->
  <New id="proxyEngineSelector" class="org.eclipse.jetty.spdy.server.proxy.ProxyEngineSelector">
    <Call name="putProxyEngine">
      <Arg>http/1.1</Arg>
      <Arg>
        <Ref refid="httpProxyEngine"/>
      </Arg>
    </Call>
    <Set name="proxyServerInfos">
      <Map>
        <Entry>
          <Item>localhost</Item>
          <Item>
            <New class="org.eclipse.jetty.spdy.server.proxy.ProxyEngineSelector$ProxyServerInfo">
              <Arg type="String">http/1.1</Arg>
              <Arg>127.0.0.1</Arg>
              <Arg type="int">9090</Arg>
            </New>
          </Item>
        </Entry>
      </Map>
    </Set>
  </New>
 
  <!-- =========================================================== -->
  <!-- These are the reverse proxy connectors accepting requests   -->
  <!-- from clients.                                               -->
  <!-- They accept non-SSL (on port 8080) and SSL (on port 8443)   -->
  <!-- HTTP, SPDY/2(HTTP) and SPDY/3(HTTP).                        -->
  <!-- Non-SPDY HTTP requests are converted to SPDY internally     -->
  <!-- and passed to the ProxyEngine above.                        -->
  <!-- =========================================================== -->
  <Call name="addConnector">
    <Arg>
      <New class="org.eclipse.jetty.spdy.server.proxy.HTTPSPDYProxyServerConnector">
        <Arg>
          <Ref refid="Server"/>
        </Arg>
        <Arg>
          <Ref refid="proxyEngineSelector"/>
        </Arg>
        <Set name="Port">8080</Set>
      </New>
    </Arg>
  </Call>
  <Call name="addConnector">
    <Arg>
      <New class="org.eclipse.jetty.spdy.server.proxy.HTTPSPDYProxyServerConnector">
        <Arg>
          <Ref refid="Server"/>
        </Arg>
        <Arg>
          <Ref refid="sslContextFactory"/>
        </Arg>
        <Arg>
          <Ref refid="proxyEngineSelector"/>
        </Arg>
        <Set name="Port">8443</Set>
      </New>
    </Arg>
  </Call>
</Configure>
  

Jetty supports both a client and a server implementation for the SPDY protocol, beginning with versions 7.6.2 and 8.1.2.

http://www.chromium.org/spdy/spdy-protocol

direct usage:

  • SPDY Client - SPDY Server

remap in/out of the box:

  • SPDY Client - SPDY PROXY - HTTP Server
  • HTTP Client - SPDY PROXY - SPDY Server

SPDY proxy

  • Jetty
  • Nginx

Jetty SPDY proxy server out of the box.

https://wiki.eclipse.org/Jetty/Feature/SPDY

but only DPSYProxyEngine supported now, https://bugs.eclipse.org/bugs/show_bug.cgi?id=396606

spdy-jetty-http also provides a fully functional SPDY proxy server out of the box. Jetty's SPDY proxy is capable to receive SPDY (currently v2/v3) and HTTP requests and proxy those requests to other SPDY servers. It will if necessary translate the incoming protocol to a protocol the target host understands. The translation to the target server protocol is done by an implementation of the ProxyEngine class. Right now we only provide a SPDYProxyEngine implementation which can talk spdy/2 and spdy/3. We're planning to support other protocols soon out of the box. As always contributions are welcome.

but, fixed in jetty9(JDK7/8)

Status: CLOSED FIXED

Reported: 2012-12-14 07:27 EST by Thomas Becker CLA Friend

Modified: 2013-02-13 11:42 EST (History)

spdy-jetty-http provides a fully functional SPDY proxy server out of the box. Jetty's SPDY proxy can receive SPDY (currently v2/v3) and HTTP requests, and proxy those requests to other SPDY servers. If necessary, an implementation of the Proxy Engine class translates the incoming protocol to a protocol the target host understands. Currently we provide a SPDYProxyEngine that can talk SPDY v2 and SPDY v3. We plan to support other protocols soon. As always, contributions are welcome.

An Example Configuration for a SPDY to HTTP Proxy, http://www.eclipse.org/jetty/documentation/9.1.3.v20140225/spdy-configuring-proxy.html

spdy-jetty-http provides full proxy functionality as described above. Here's another example configuration for a SPDY to HTTP proxy. This proxy accepts SPDY requests and proxies them to an HTTP server.

This is a very common use case, for example to terminate SPDY on a frontend server when you need to talk plain HTTP to your backend, because either your network hardware needs to inspect HTTP content or your backend is unable to talk SPDY. You have the performance advantages of SPDY over the slow internet with high latencies, and you talk HTTP to your backend as needed.

Nginx with SPDY

http://nginx.org/en/docs/http/ngx_http_spdy_module.html

http://www.nginxtips.com/how-to-install-and-configure-spdy-on-nginx/

./configure --with-http_spdy_module --with-http_ssl_module --with-pcre=~/pcre-8.36 --with-openssl=~/openssl-1.0.1j/
worker_processes  1;

error_log  /home/houkun/error.log;
pid       /home/houkun/nginx.pid;


events {
    worker_connections  1024;
}


http {
    server {
        listen       4433 ssl spdy;
        server_name  localhost;

        ssl_certificate      /home/houkun/stunnel.pem;
        ssl_certificate_key  /home/houkun/stunnel.pem;

        location / {
            proxy_pass http://10.2.14.243:8080;
        }
    }
}

QQ20141223-3

curl "https://10.189.226.27/router/rest?app_key=4272&format=json&id=1&method=taobao.time.get&partner_id=sdk-bash&timestamp=1419301233&v=2.0&sign=95DDD0F9DAB08ECD5F50DD2660828049"

QQ20141223-4

chrome://net-internals/#spdy

SPDY Client

Netty4

https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/example/spdy/client/SpdyClient.java

OkHttp

OkHttp An HTTP & SPDY client for Android and Java applications http://square.github.io/okhttp/

You can try out OkHttp without rewriting your network code. The okhttp-urlconnection module implements the familiar java.net.HttpURLConnection API and the okhttp-apache module implements the Apache HttpClient API.

OkHttp supports Android 2.3 and above. For Java, the minimum requirement is 1.7.

https://github.com/square/okhttp/tree/master/okhttp-urlconnection/src/main/java/com/squareup/okhttp/internal/huc

Jetty

http://www.eclipse.org/jetty/documentation/current/spdy.html

Jetty's SPDY modules Jetty supports both a client and a server implementation for the SPDY protocol, beginning with versions 7.6.2 and 8.1.2. As Jetty 7 and 8 are now in maintenance-only mode, we recommend using Jetty 9 if you intend to use SPDY. Not all features described here exist in 7/8. To provide the best support possible for SPDY, the Jetty project also provides an implementation for NPN. Both the SPDY and the NPN implementations require OpenJDK 1.7 or greater.

SPDY_SESSION_UPDATE_RECV_WINDOW after SPDY_SESSION_RECV_DATA

http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1#TOC-Initial-Flow-Control-Window-Size

https://www.belshe.com/2012/03/29/comments-on-microsofts-spdy-proposal/

c) Removal of Flow Control The Microsoft proposal is quick to dismiss SPDY’s per-stream flow control as though it is already handled at the TCP layer. However, this is incorrect. TCP handles flow control for the TCP stream. Because SPDY introduces multiple concurrent flows, a new layer of flow control is necessary. Imagine you were sending 10 streams to a server, and one of those streams stalled out (for whatever reason). Without flow control, you either have to terminate all the streams, buffer unbounded amounts of memory, or stall all the streams. None of these are good outcomes, and TCP’s flow control is not the same as SPDY’s flow control.

This may be an example of where SPDY’s implementation experience trumps any amount of protocol theory. For those who remember, earlier drafts of SPDY didn’t have flow control. We were aware of it long ago, but until we fully implemented SPDY, we didn’t know how badly it was needed nor how to do it in a performant and simple manner. I can’t emphasize enough with protocols how important it is to actually implement your proposals. If you don’t implement them, you don’t really know if it works.

http://chimera.labs.oreilly.com/books/1230000000545/ch12.html

Does the preceding list remind you of TCP flow control? It should; the mechanism is effectively identical—see “Flow Control”. However, because TCP flow control cannot differentiate among the many streams within a single HTTP 2.0 connection, it is insufficient on its own. Hence the reason for HTTP 2.0 flow control.

The HTTP 2.0 standard does not specify any specific algorithm, values, or when the WINDOW_UPDATE frames should be sent: the implementers are able to select their own algorithm to match their use case and deliver the best performance.

@wsky
Copy link
Author

wsky commented Dec 24, 2014

stream rt

t=320730 [st=320477]    SPDY_SESSION_SYN_STREAM
                        --> fin = true
                        --> :host: 10.189.226.27
                            :method: GET
                            :path: /router/rest?app_key=4272&format=json&id=1&method=taobao.time.get&partner_id=sdk-bash&timestamp=1419301233&v=2.0&sign=95DDD0F9DAB08ECD5F50DD2660828049
                            :scheme: https
                            :version: HTTP/1.1
                            accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
                            accept-encoding: gzip, deflate, sdch
                            accept-language: zh-CN,zh;q=0.8,en;q=0.6
                            cache-control: max-age=0
                            ra-sid: 2A784AC3-20140730-021140-a20a16-faa2ee
                            ra-ver: 2.8.6
                            user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
                        --> spdy_priority = 0
                        --> stream_id = 21
                        --> unidirectional = false
t=320734 [st=320481]    SPDY_SESSION_PING
                        --> is_ack = false
                        --> type = "received"
                        --> unique_id = 11
t=320746 [st=320493]    SPDY_SESSION_SYN_REPLY
                        --> fin = false
                        --> :status: 200 OK
                            :version: HTTP/1.1
                            application-host: 10.189.197.8
                            content-encoding: gzip
                            content-type: text/javascript;charset=UTF-8
                            date: Tue, 23 Dec 2014 05:12:57 GMT
                            eagleeye-traceid-2nd: 0abde21b14193115775316507e
                            server: nginx/1.6.2
                            top-bodylength: 52
                            vary: Accept-Encoding
                        --> stream_id = 21
t=320747 [st=320494]    SPDY_SESSION_RECV_DATA
                        --> fin = false
                        --> size = 69
                        --> stream_id = 21
t=320747 [st=320494]    SPDY_SESSION_UPDATE_RECV_WINDOW
                        --> delta = -69
                        --> window_size = 10485691
t=320747 [st=320494]    SPDY_SESSION_RECV_DATA
                        --> fin = true
                        --> size = 0
                        --> stream_id = 21
t=320747 [st=320494]    SPDY_STREAM_UPDATE_RECV_WINDOW
                        --> delta = 69
                        --> window_size = 10485760

@wsky
Copy link
Author

wsky commented Dec 24, 2014

NPN
https://technotes.googlecode.com/git/nextprotoneg.html

Jetty NPN jdk7+
http://wiki.eclipse.org/Jetty/Feature/NPN
https://github.com/jetty-project/jetty-npn

http://docs.oracle.com/javase/7/docs/webnotes/adoptionGuide/index.html

Support for TLS 1.1
The SunJSSE provider now supports TLS 1.1 as described in RFC 4346. The most important update is protection against cipher block chaining (CBC) attacks.

Support for TLS 1.2
The SunJSSE provider now supports TLS 1.2 as described in RFC 5246. Among other things, it specifies different internal hashing algorithms, adds new cipher suites, and contains improved flexibility, particularly for negotiation of cryptographic algorithms.

TLS renegotiation
As of JDK 7, Java SE supports RFC 5746, which fixes a renegotiation issue in the TLS protocol. For more information, see Transport Layer Security (TLS) Renegotiation Issue (JDK 7 Guides).

Transport Layer Security (TLS) Renegotiation Issue
http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html#tlsRenegotiation

ALPN
http://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation

@wsky
Copy link
Author

wsky commented Dec 30, 2014

com.squareup.okhttp.ConnectionPool

Manages reuse of HTTP and SPDY connections for reduced network latency. HTTP requests that share the same >com.squareup.okhttp.Address may share a com.squareup.okhttp.Connection. This class implements the policy of which connections to keep open for future use.

The system-wide default uses system properties for tuning parameters:

http.keepAlive true if HTTP and SPDY connections should be pooled at all. Default is true.

http.maxConnections maximum number of idle connections to each to keep in the pool. Default is 5.

http.keepAliveDuration Time in milliseconds to keep the connection alive in the pool before closing it. Default is 5 minutes. >This property isn't used by HttpURLConnection.
The default instance doesn't adjust its configuration as system properties are changed. This assumes that the applications that set these parameters do so before making HTTP connections, and that this class is initialized lazily.

@wsky
Copy link
Author

wsky commented Dec 30, 2014

find out SPDY used actually

com.squareup.okhttp.Connection

private void upgradeToTls(Request tunnelRequest, int readTimeout, int writeTimeout) {
// ...

String maybeProtocol;
    if (route.connectionSpec.supportsTlsExtensions()
        && (maybeProtocol = platform.getSelectedProtocol(sslSocket)) != null) {
      protocol = Protocol.get(maybeProtocol); // Throws IOE on unknown.
    }

    if (protocol == Protocol.SPDY_3 || protocol == Protocol.HTTP_2) {
      sslSocket.setSoTimeout(0); // SPDY timeouts are set per-stream.
      spdyConnection = new SpdyConnection.Builder(route.address.getUriHost(), true, socket)
          .protocol(protocol).build();
      spdyConnection.sendConnectionPreface();
    } else {
      httpConnection = new HttpConnection(pool, this, socket);
    }
}

com.squareup.okhttp.internal.Platform

private static final Platform PLATFORM = findPlatform();

  public static Platform get() {
    return PLATFORM;
  }

try { // to find the Jetty's ALPN or NPN extension for OpenJDK.
      String negoClassName = "org.eclipse.jetty.alpn.ALPN";
      Class<?> negoClass;
      try {
        negoClass = Class.forName(negoClassName);
      } catch (ClassNotFoundException ignored) { // ALPN isn't on the classpath.
        negoClassName = "org.eclipse.jetty.npn.NextProtoNego";
        negoClass = Class.forName(negoClassName);
      }
      Class<?> providerClass = Class.forName(negoClassName + "$Provider");
      Class<?> clientProviderClass = Class.forName(negoClassName + "$ClientProvider");
      Class<?> serverProviderClass = Class.forName(negoClassName + "$ServerProvider");
      Method putMethod = negoClass.getMethod("put", SSLSocket.class, providerClass);
      Method getMethod = negoClass.getMethod("get", SSLSocket.class);
      return new JdkWithJettyBootPlatform(
          putMethod, getMethod, clientProviderClass, serverProviderClass);
    } catch (ClassNotFoundException ignored) { // NPN isn't on the classpath.
    } catch (NoSuchMethodException ignored) { // The ALPN or NPN version isn't what we expect.
    }

@wsky
Copy link
Author

wsky commented Dec 30, 2014

<plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <argLine>-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn.version}/npn-boot-${npn.version}.jar</argLine>
                </configuration>
            </plugin>
<dependency>
            <groupId>org.mortbay.jetty.npn</groupId>
            <artifactId>npn-boot</artifactId>
            <version>${npn.version}</version>
        </dependency>

@wsky
Copy link
Author

wsky commented Dec 30, 2014

https://github.com/square/okhttp/wiki/Building

-Xbootclasspath/p:/Users/jwilson/.m2/repository/org/mortbay/jetty/npn/npn-boot/1.1.7.v20140316/npn-boot-1.1.7.v20140316.jar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment