New issue
Advanced search Search tips

Issue 869876 link

Starred by 1 user

Issue metadata

Status: Untriaged
Owner: ----
Components:
EstimatedDays: ----
NextAction: ----
OS: ----
Pri: 3
Type: Feature



Sign in to add a comment

BidirectionalStream backup impl

Project Member Reported by pauljensen@chromium.org, Aug 1

Issue description

Sometimes Cronet fails to load.  There are various reasons why this happens (out of date shared libraries, broken app installations, misconfigured devices with the wrong shared libraries) and we've tried to solve or mitigate most of them, but Cronet still fails to load sometimes.  Many Cronet embedders realize this and have a backup plan for what to do when Cronet doesn't load.  Some embedders fall back to using Cronet's java-only fallback UrlRequest impl that's based on the Android system HttpURLConnection API.  Currently there is no BidirectionalStream backup implementation, which forces some embedders to turn off all GRPC-based features when Cronet fails to load.

We've talked for years about having a backup java-only BidirectionalStream impl.  I wanted to file a bug to track ideas and progress related to this initiative.

Before progressing towards an implementation I think we should do a little due diligence and see if there is demand for such a fallback impl.

I did a little bit of thinking about whether a BidirectionalStream fallback impl is even possible:

Being a fallback impl, performance and robustness and complete compatibility are low priorities, while general compatibility and small size are high priorities.

I think use of QUIC is off the table, to keep it simple.
I think it would have to be based on HTTP/2 over TLS over TCP.
To be useful to embedders it would need to be secure and encrypted.

HTTP/2 can be negotiated one of a few ways:
1. Via an Upgrade header from HTTP/1.1.  I don't think this is viable because I believe many servers only support this via cleartext which isn't useful to embedders.
2. Via NPN.  I don't think this is viable as NPN deprecation was announced in 2013 and server support may disappear at any time.
3. Via ALPN.

ALPN support was added in Android KitKat (see public b/36973797) though it looks like the API is still hidden today (android.net.SSLCertificateSocketFactory.setAlpnProtocols()).
This means that without major heroics unsuitable for a space-constrained backup implementation, ALPN and hence HTTP/2 and this back up impl may only be viable for 96% of Android devices (the public Android dashboard (https://developer.android.com/about/dashboards/) indicates 96% of devices are KitKat+).
 
I did a little experimenting and was able to communicate with Google via HTTP/2 negotiated via ALPN:

                try {
                    SSLCertificateSocketFactory f = new SSLCertificateSocketFactory(10000);
                    byte[][] protocols = new byte[][]{"h2".getBytes()};
                    Method setAlpnProtocols =
                            f.getClass().getDeclaredMethod("setAlpnProtocols", byte[][].class);
                    setAlpnProtocols.invoke(f, new Object[]{protocols});
                    Socket s = f.createSocket("www.google.com", 443);
                    Log.e("PJPJ", "socket connected!");
                    OutputStream o = s.getOutputStream();
                    InputStream is = s.getInputStream();
                    // send connection preface, see HTTP/2 section 3.5
                    o.write(new byte[]{
                            0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x32,
                            0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a
                    });
                    // send empty settings frame as required by spec
                    o.write(new byte[]{
                            0x00, 0x00, 0x00, // 0 sized payload
                            0x04, // settings frame
                            0x00, // no flags
                            0x00, 0x00, 0x00, 0x00 // 0 stream identifier
                    });
                    // header frame
                    o.write(new byte[]{
                            0x00, 0x00, 0x03, // 3 sized payload
                            0x01, // headers frame
                            0x05, // EOS, EOH flags
                            0x00, 0x00, 0x00, 0x01, // 1 stream identifier
                            (byte)0x82, // :method: GET
                            (byte)0x84, // :path: /
                            (byte)0x87 // :scheme: https
                    });
                    while(true) {
                        String st = "";
                        byte[] bs = new byte[3];
                        is.read(bs);
                        for (int i = 0; i < 3; i++) {
                            st = st + " " + bs[i];
                        }
                        byte[] b = new byte[bs[2] + 9 - 3];
                        is.read(b);
                        for (int i = 0; i < b.length; i++) {
                            st = st + " " + b[i];
                        }
                        Log.e("PJPJ", "Read: " + st);
                    }
                } catch (Exception e) {
                    Log.e("PJPJ", "bad", e);
                }
From logging I could see Google sending me back multiple HTTP/2 frames:
08-03 14:34:15.977  3933  3986 E PJPJ    : socket connected!
08-03 14:34:16.026  3933  3986 E PJPJ    : Read:  0 0 18 4 0 0 0 0 0 0 3 0 0 0 100 0 4 0 16 0 0 0 6 0 0 64 0
08-03 14:34:16.026  3933  3986 E PJPJ    : Read:  0 0 4 8 0 0 0 0 0 0 15 0 1
08-03 14:34:16.026  3933  3986 E PJPJ    : Read:  0 0 0 4 1 0 0 0 0
08-03 14:34:16.028  3933  3986 E PJPJ    : Read:  0 0 76 1 4 0 0 0 1 -116 95 -110 73 124 -91 -119 -45 77 31 106 18 113 -40 -126 -90 14 27 -16 -84 -9 64 -117 -80 -78 -106 -53 11 98 -43 -98 -125 19 -41 -120 -88 -21 88 89 75 101 -123 -77 92 -125 11 109 -73 0 -125 -112 105 47 -106 -61 97 -66 -108 3 42 67 108 -54 8 1 121 64 -67 113 -106 -82 5 -59 49 104 -33
08-03 14:34:16.028  3933  3986 E PJPJ    : Read:  0 0 4 3 0 0 0 0 1 0 0 0 1
08-03 14:38:15.188  3933  3986 E PJPJ    : Read:  0 0 25 7 0 0 0 0 0 0 0 0 1 0 0 0 0 115 101 115 115 105 111 110 95 116 105 109 101 100 95 111 117 116
08-03 14:38:15.190  3933  3986 E PJPJ    : Read:  0 0 0 0 0 0 0 0 0
08-03 14:38:23.866  3933  3986 E PJPJ    : Read:  0 0 0 0 0 0 0 0 0
08-03 14:38:23.866  3933  3986 E PJPJ    : Read:  0 0 0 0 0 0 0 0 0

Worked a bit more on my prototype:
https://chromium-review.googlesource.com/c/chromium/src/+/1167905
HPACK decoding seems to work.
It can now successfully communicate with www.google.com.

Sign in to add a comment