1/* 2 * Copyright (C) 2012 Square, Inc. 3 * Copyright (C) 2012 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17package com.squareup.okhttp.internal; 18 19import dalvik.system.SocketTagger; 20import java.io.IOException; 21import java.io.OutputStream; 22import java.net.InetSocketAddress; 23import java.net.Socket; 24import java.net.SocketException; 25import java.net.URI; 26import java.net.URISyntaxException; 27import java.net.URL; 28import java.util.List; 29import javax.net.ssl.SSLSocket; 30 31import com.squareup.okhttp.Protocol; 32 33import okio.Buffer; 34 35/** 36 * Access to proprietary Android APIs. Doesn't use reflection. 37 */ 38public final class Platform { 39 private static final Platform PLATFORM = new Platform(); 40 41 public static Platform get() { 42 return PLATFORM; 43 } 44 45 /** setUseSessionTickets(boolean) */ 46 private static final OptionalMethod<Socket> SET_USE_SESSION_TICKETS = 47 new OptionalMethod<Socket>(null, "setUseSessionTickets", Boolean.TYPE); 48 /** setHostname(String) */ 49 private static final OptionalMethod<Socket> SET_HOSTNAME = 50 new OptionalMethod<Socket>(null, "setHostname", String.class); 51 /** byte[] getAlpnSelectedProtocol() */ 52 private static final OptionalMethod<Socket> GET_ALPN_SELECTED_PROTOCOL = 53 new OptionalMethod<Socket>(byte[].class, "getAlpnSelectedProtocol"); 54 /** setAlpnSelectedProtocol(byte[]) */ 55 private static final OptionalMethod<Socket> SET_ALPN_PROTOCOLS = 56 new OptionalMethod<Socket>(null, "setAlpnProtocols", byte[].class ); 57 58 public void logW(String warning) { 59 System.logW(warning); 60 } 61 62 public void tagSocket(Socket socket) throws SocketException { 63 SocketTagger.get().tag(socket); 64 } 65 66 public void untagSocket(Socket socket) throws SocketException { 67 SocketTagger.get().untag(socket); 68 } 69 70 public URI toUriLenient(URL url) throws URISyntaxException { 71 return url.toURILenient(); 72 } 73 74 public void configureTlsExtensions( 75 SSLSocket sslSocket, String hostname, List<Protocol> protocols) { 76 // Enable SNI and session tickets. 77 if (hostname != null) { 78 SET_USE_SESSION_TICKETS.invokeOptionalWithoutCheckedException(sslSocket, true); 79 SET_HOSTNAME.invokeOptionalWithoutCheckedException(sslSocket, hostname); 80 } 81 82 // Enable ALPN. 83 boolean alpnSupported = SET_ALPN_PROTOCOLS.isSupported(sslSocket); 84 if (!alpnSupported) { 85 return; 86 } 87 88 Object[] parameters = { concatLengthPrefixed(protocols) }; 89 if (alpnSupported) { 90 SET_ALPN_PROTOCOLS.invokeWithoutCheckedException(sslSocket, parameters); 91 } 92 } 93 94 /** 95 * Called after the TLS handshake to release resources allocated by {@link 96 * #configureTlsExtensions}. 97 */ 98 public void afterHandshake(SSLSocket sslSocket) { 99 } 100 101 public String getSelectedProtocol(SSLSocket socket) { 102 boolean alpnSupported = GET_ALPN_SELECTED_PROTOCOL.isSupported(socket); 103 if (!alpnSupported) { 104 return null; 105 } 106 107 byte[] alpnResult = 108 (byte[]) GET_ALPN_SELECTED_PROTOCOL.invokeWithoutCheckedException(socket); 109 if (alpnResult != null) { 110 return new String(alpnResult, Util.UTF_8); 111 } 112 return null; 113 } 114 115 public void connectSocket(Socket socket, InetSocketAddress address, 116 int connectTimeout) throws IOException { 117 socket.connect(address, connectTimeout); 118 } 119 120 /** Prefix used on custom headers. */ 121 public String getPrefix() { 122 return "X-Android"; 123 } 124 125 /** 126 * Returns the concatenation of 8-bit, length prefixed protocol names. 127 * http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4 128 */ 129 static byte[] concatLengthPrefixed(List<Protocol> protocols) { 130 Buffer result = new Buffer(); 131 for (int i = 0, size = protocols.size(); i < size; i++) { 132 Protocol protocol = protocols.get(i); 133 if (protocol == Protocol.HTTP_1_0) continue; // No HTTP/1.0 for ALPN. 134 result.writeByte(protocol.toString().length()); 135 result.writeUtf8(protocol.toString()); 136 } 137 return result.readByteArray(); 138 } 139} 140