12231db3e6bb54447a9b14cf004a6cb03c373651cjwilson/*
22231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * Copyright (C) 2012 Square, Inc.
32231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * Copyright (C) 2012 The Android Open Source Project
42231db3e6bb54447a9b14cf004a6cb03c373651cjwilson *
52231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * Licensed under the Apache License, Version 2.0 (the "License");
62231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * you may not use this file except in compliance with the License.
72231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * You may obtain a copy of the License at
82231db3e6bb54447a9b14cf004a6cb03c373651cjwilson *
92231db3e6bb54447a9b14cf004a6cb03c373651cjwilson *      http://www.apache.org/licenses/LICENSE-2.0
102231db3e6bb54447a9b14cf004a6cb03c373651cjwilson *
112231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * Unless required by applicable law or agreed to in writing, software
122231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * distributed under the License is distributed on an "AS IS" BASIS,
132231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
142231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * See the License for the specific language governing permissions and
152231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * limitations under the License.
162231db3e6bb54447a9b14cf004a6cb03c373651cjwilson */
172231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpackage com.squareup.okhttp.internal;
182231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
192231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport dalvik.system.SocketTagger;
20a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport java.io.IOException;
212231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.io.OutputStream;
22a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport java.net.InetSocketAddress;
232231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.Socket;
242231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.SocketException;
252231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.URI;
262231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.URISyntaxException;
272231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.URL;
283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.List;
292231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.SSLSocket;
303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Protocol;
32565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller
33e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Buffer;
342231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
352231db3e6bb54447a9b14cf004a6cb03c373651cjwilson/**
362231db3e6bb54447a9b14cf004a6cb03c373651cjwilson * Access to proprietary Android APIs. Doesn't use reflection.
372231db3e6bb54447a9b14cf004a6cb03c373651cjwilson */
382231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpublic final class Platform {
392231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    private static final Platform PLATFORM = new Platform();
402231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
412231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    public static Platform get() {
422231db3e6bb54447a9b14cf004a6cb03c373651cjwilson        return PLATFORM;
432231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
442231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
45565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller    /** setUseSessionTickets(boolean) */
46565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller    private static final OptionalMethod<Socket> SET_USE_SESSION_TICKETS =
47565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller            new OptionalMethod<Socket>(null, "setUseSessionTickets", Boolean.TYPE);
48565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller    /** setHostname(String) */
49565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller    private static final OptionalMethod<Socket> SET_HOSTNAME =
50565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller            new OptionalMethod<Socket>(null, "setHostname", String.class);
51565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller    /** byte[] getAlpnSelectedProtocol() */
52565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller    private static final OptionalMethod<Socket> GET_ALPN_SELECTED_PROTOCOL =
53565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller            new OptionalMethod<Socket>(byte[].class, "getAlpnSelectedProtocol");
54565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller    /** setAlpnSelectedProtocol(byte[]) */
55565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller    private static final OptionalMethod<Socket> SET_ALPN_PROTOCOLS =
56565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller            new OptionalMethod<Socket>(null, "setAlpnProtocols", byte[].class );
57565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller
582231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    public void logW(String warning) {
592231db3e6bb54447a9b14cf004a6cb03c373651cjwilson        System.logW(warning);
602231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
612231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
622231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    public void tagSocket(Socket socket) throws SocketException {
632231db3e6bb54447a9b14cf004a6cb03c373651cjwilson        SocketTagger.get().tag(socket);
642231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
652231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
662231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    public void untagSocket(Socket socket) throws SocketException {
672231db3e6bb54447a9b14cf004a6cb03c373651cjwilson        SocketTagger.get().untag(socket);
682231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
692231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
702231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    public URI toUriLenient(URL url) throws URISyntaxException {
712231db3e6bb54447a9b14cf004a6cb03c373651cjwilson        return url.toURILenient();
722231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
732231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
74e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    public void configureTlsExtensions(
75e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            SSLSocket sslSocket, String hostname, List<Protocol> protocols) {
76e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        // Enable SNI and session tickets.
77e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        if (hostname != null) {
78e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            SET_USE_SESSION_TICKETS.invokeOptionalWithoutCheckedException(sslSocket, true);
79e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            SET_HOSTNAME.invokeOptionalWithoutCheckedException(sslSocket, hostname);
80e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        }
81e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
8266d489eeddae6aafbbcdf9cda71e16b4a9a33a0fNeil Fuller        // Enable ALPN.
83e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        boolean alpnSupported = SET_ALPN_PROTOCOLS.isSupported(sslSocket);
84e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        if (!alpnSupported) {
85e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            return;
86e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        }
87e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
88e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        Object[] parameters = { concatLengthPrefixed(protocols) };
89e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        if (alpnSupported) {
90e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            SET_ALPN_PROTOCOLS.invokeWithoutCheckedException(sslSocket, parameters);
919da11daf5d24272fe8733de3e1d9587425d17c6eNeil Fuller        }
922231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
932231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
942231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    /**
95e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller     * Called after the TLS handshake to release resources allocated by {@link
96e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller     * #configureTlsExtensions}.
972231db3e6bb54447a9b14cf004a6cb03c373651cjwilson     */
98e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    public void afterHandshake(SSLSocket sslSocket) {
99e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
100e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
101e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    public String getSelectedProtocol(SSLSocket socket) {
102565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller        boolean alpnSupported = GET_ALPN_SELECTED_PROTOCOL.isSupported(socket);
1031fa00b75f644dc81c627fdb75c898f4f8ad48cc1Neil Fuller        if (!alpnSupported) {
1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            return null;
1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1071fa00b75f644dc81c627fdb75c898f4f8ad48cc1Neil Fuller        byte[] alpnResult =
108e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller                (byte[]) GET_ALPN_SELECTED_PROTOCOL.invokeWithoutCheckedException(socket);
1091fa00b75f644dc81c627fdb75c898f4f8ad48cc1Neil Fuller        if (alpnResult != null) {
110e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            return new String(alpnResult, Util.UTF_8);
111565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller        }
112565c2ad69684ef7c23b5aaa3b7f0c7363cef6bd7Neil Fuller        return null;
1132231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
1142231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
115a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    public void connectSocket(Socket socket, InetSocketAddress address,
116a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath              int connectTimeout) throws IOException {
117a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        socket.connect(address, connectTimeout);
118a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    }
119a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
120a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    /** Prefix used on custom headers. */
121a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    public String getPrefix() {
122a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        return "X-Android";
123a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    }
1243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    /**
126e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller     * Returns the concatenation of 8-bit, length prefixed protocol names.
1273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller     * http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4
1283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller     */
1293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    static byte[] concatLengthPrefixed(List<Protocol> protocols) {
130e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        Buffer result = new Buffer();
131e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        for (int i = 0, size = protocols.size(); i < size; i++) {
132e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            Protocol protocol = protocols.get(i);
13366d489eeddae6aafbbcdf9cda71e16b4a9a33a0fNeil Fuller            if (protocol == Protocol.HTTP_1_0) continue; // No HTTP/1.0 for ALPN.
134e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            result.writeByte(protocol.toString().length());
135e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            result.writeUtf8(protocol.toString());
1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
137e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        return result.readByteArray();
1383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1392231db3e6bb54447a9b14cf004a6cb03c373651cjwilson}
140