PageRenderTime 375ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/pushWithPythonServer/pushnotification/platforms/android/CordovaLib/src/com/squareup/okhttp/internal/Platform.java

https://gitlab.com/bsan/ionicDev
Java | 370 lines | 270 code | 35 blank | 65 comment | 36 complexity | f21b67c48f83bfe2fb015beb5c69b193 MD5 | raw file
  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. */
  17. package com.squareup.okhttp.internal;
  18. import java.io.IOException;
  19. import java.io.OutputStream;
  20. import java.io.UnsupportedEncodingException;
  21. import java.lang.reflect.Constructor;
  22. import java.lang.reflect.InvocationHandler;
  23. import java.lang.reflect.InvocationTargetException;
  24. import java.lang.reflect.Method;
  25. import java.lang.reflect.Proxy;
  26. import java.net.InetSocketAddress;
  27. import java.net.Socket;
  28. import java.net.SocketException;
  29. import java.net.URI;
  30. import java.net.URISyntaxException;
  31. import java.net.URL;
  32. import java.util.ArrayList;
  33. import java.util.List;
  34. import java.util.logging.Level;
  35. import java.util.logging.Logger;
  36. import java.util.zip.Deflater;
  37. import java.util.zip.DeflaterOutputStream;
  38. import javax.net.ssl.SSLSocket;
  39. /**
  40. * Access to Platform-specific features necessary for SPDY and advanced TLS.
  41. *
  42. * <h3>SPDY</h3>
  43. * SPDY requires a TLS extension called NPN (Next Protocol Negotiation) that's
  44. * available in Android 4.1+ and OpenJDK 7+ (with the npn-boot extension). It
  45. * also requires a recent version of {@code DeflaterOutputStream} that is
  46. * public API in Java 7 and callable via reflection in Android 4.1+.
  47. */
  48. public class Platform {
  49. private static final Platform PLATFORM = findPlatform();
  50. private Constructor<DeflaterOutputStream> deflaterConstructor;
  51. public static Platform get() {
  52. return PLATFORM;
  53. }
  54. /** Prefix used on custom headers. */
  55. public String getPrefix() {
  56. return "OkHttp";
  57. }
  58. public void logW(String warning) {
  59. System.out.println(warning);
  60. }
  61. public void tagSocket(Socket socket) throws SocketException {
  62. }
  63. public void untagSocket(Socket socket) throws SocketException {
  64. }
  65. public URI toUriLenient(URL url) throws URISyntaxException {
  66. return url.toURI(); // this isn't as good as the built-in toUriLenient
  67. }
  68. /**
  69. * Attempt a TLS connection with useful extensions enabled. This mode
  70. * supports more features, but is less likely to be compatible with older
  71. * HTTPS servers.
  72. */
  73. public void enableTlsExtensions(SSLSocket socket, String uriHost) {
  74. }
  75. /**
  76. * Attempt a secure connection with basic functionality to maximize
  77. * compatibility. Currently this uses SSL 3.0.
  78. */
  79. public void supportTlsIntolerantServer(SSLSocket socket) {
  80. socket.setEnabledProtocols(new String[] {"SSLv3"});
  81. }
  82. /** Returns the negotiated protocol, or null if no protocol was negotiated. */
  83. public byte[] getNpnSelectedProtocol(SSLSocket socket) {
  84. return null;
  85. }
  86. /**
  87. * Sets client-supported protocols on a socket to send to a server. The
  88. * protocols are only sent if the socket implementation supports NPN.
  89. */
  90. public void setNpnProtocols(SSLSocket socket, byte[] npnProtocols) {
  91. }
  92. public void connectSocket(Socket socket, InetSocketAddress address,
  93. int connectTimeout) throws IOException {
  94. socket.connect(address, connectTimeout);
  95. }
  96. /**
  97. * Returns a deflater output stream that supports SYNC_FLUSH for SPDY name
  98. * value blocks. This throws an {@link UnsupportedOperationException} on
  99. * Java 6 and earlier where there is no built-in API to do SYNC_FLUSH.
  100. */
  101. public OutputStream newDeflaterOutputStream(OutputStream out, Deflater deflater,
  102. boolean syncFlush) {
  103. try {
  104. Constructor<DeflaterOutputStream> constructor = deflaterConstructor;
  105. if (constructor == null) {
  106. constructor = deflaterConstructor = DeflaterOutputStream.class.getConstructor(
  107. OutputStream.class, Deflater.class, boolean.class);
  108. }
  109. return constructor.newInstance(out, deflater, syncFlush);
  110. } catch (NoSuchMethodException e) {
  111. throw new UnsupportedOperationException("Cannot SPDY; no SYNC_FLUSH available");
  112. } catch (InvocationTargetException e) {
  113. throw e.getCause() instanceof RuntimeException ? (RuntimeException) e.getCause()
  114. : new RuntimeException(e.getCause());
  115. } catch (InstantiationException e) {
  116. throw new RuntimeException(e);
  117. } catch (IllegalAccessException e) {
  118. throw new AssertionError();
  119. }
  120. }
  121. /** Attempt to match the host runtime to a capable Platform implementation. */
  122. private static Platform findPlatform() {
  123. // Attempt to find Android 2.3+ APIs.
  124. Class<?> openSslSocketClass;
  125. Method setUseSessionTickets;
  126. Method setHostname;
  127. try {
  128. try {
  129. openSslSocketClass = Class.forName("com.android.org.conscrypt.OpenSSLSocketImpl");
  130. } catch (ClassNotFoundException ignored) {
  131. // Older platform before being unbundled.
  132. openSslSocketClass = Class.forName(
  133. "org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl");
  134. }
  135. setUseSessionTickets = openSslSocketClass.getMethod("setUseSessionTickets", boolean.class);
  136. setHostname = openSslSocketClass.getMethod("setHostname", String.class);
  137. // Attempt to find Android 4.1+ APIs.
  138. try {
  139. Method setNpnProtocols = openSslSocketClass.getMethod("setNpnProtocols", byte[].class);
  140. Method getNpnSelectedProtocol = openSslSocketClass.getMethod("getNpnSelectedProtocol");
  141. return new Android41(openSslSocketClass, setUseSessionTickets, setHostname,
  142. setNpnProtocols, getNpnSelectedProtocol);
  143. } catch (NoSuchMethodException ignored) {
  144. return new Android23(openSslSocketClass, setUseSessionTickets, setHostname);
  145. }
  146. } catch (ClassNotFoundException ignored) {
  147. // This isn't an Android runtime.
  148. } catch (NoSuchMethodException ignored) {
  149. // This isn't Android 2.3 or better.
  150. }
  151. // Attempt to find the Jetty's NPN extension for OpenJDK.
  152. try {
  153. String npnClassName = "org.eclipse.jetty.npn.NextProtoNego";
  154. Class<?> nextProtoNegoClass = Class.forName(npnClassName);
  155. Class<?> providerClass = Class.forName(npnClassName + "$Provider");
  156. Class<?> clientProviderClass = Class.forName(npnClassName + "$ClientProvider");
  157. Class<?> serverProviderClass = Class.forName(npnClassName + "$ServerProvider");
  158. Method putMethod = nextProtoNegoClass.getMethod("put", SSLSocket.class, providerClass);
  159. Method getMethod = nextProtoNegoClass.getMethod("get", SSLSocket.class);
  160. return new JdkWithJettyNpnPlatform(
  161. putMethod, getMethod, clientProviderClass, serverProviderClass);
  162. } catch (ClassNotFoundException ignored) {
  163. // NPN isn't on the classpath.
  164. } catch (NoSuchMethodException ignored) {
  165. // The NPN version isn't what we expect.
  166. }
  167. return new Platform();
  168. }
  169. /** Android version 2.3 and newer support TLS session tickets and server name indication (SNI). */
  170. private static class Android23 extends Platform {
  171. protected final Class<?> openSslSocketClass;
  172. private final Method setUseSessionTickets;
  173. private final Method setHostname;
  174. private Android23(
  175. Class<?> openSslSocketClass, Method setUseSessionTickets, Method setHostname) {
  176. this.openSslSocketClass = openSslSocketClass;
  177. this.setUseSessionTickets = setUseSessionTickets;
  178. this.setHostname = setHostname;
  179. }
  180. @Override public void connectSocket(Socket socket, InetSocketAddress address,
  181. int connectTimeout) throws IOException {
  182. try {
  183. socket.connect(address, connectTimeout);
  184. } catch (SecurityException se) {
  185. // Before android 4.3, socket.connect could throw a SecurityException
  186. // if opening a socket resulted in an EACCES error.
  187. IOException ioException = new IOException("Exception in connect");
  188. ioException.initCause(se);
  189. throw ioException;
  190. }
  191. }
  192. @Override public void enableTlsExtensions(SSLSocket socket, String uriHost) {
  193. super.enableTlsExtensions(socket, uriHost);
  194. if (openSslSocketClass.isInstance(socket)) {
  195. // This is Android: use reflection on OpenSslSocketImpl.
  196. try {
  197. setUseSessionTickets.invoke(socket, true);
  198. setHostname.invoke(socket, uriHost);
  199. } catch (InvocationTargetException e) {
  200. throw new RuntimeException(e);
  201. } catch (IllegalAccessException e) {
  202. throw new AssertionError(e);
  203. }
  204. }
  205. }
  206. }
  207. /** Android version 4.1 and newer support NPN. */
  208. private static class Android41 extends Android23 {
  209. private final Method setNpnProtocols;
  210. private final Method getNpnSelectedProtocol;
  211. private Android41(Class<?> openSslSocketClass, Method setUseSessionTickets, Method setHostname,
  212. Method setNpnProtocols, Method getNpnSelectedProtocol) {
  213. super(openSslSocketClass, setUseSessionTickets, setHostname);
  214. this.setNpnProtocols = setNpnProtocols;
  215. this.getNpnSelectedProtocol = getNpnSelectedProtocol;
  216. }
  217. @Override public void setNpnProtocols(SSLSocket socket, byte[] npnProtocols) {
  218. if (!openSslSocketClass.isInstance(socket)) {
  219. return;
  220. }
  221. try {
  222. setNpnProtocols.invoke(socket, new Object[] {npnProtocols});
  223. } catch (IllegalAccessException e) {
  224. throw new AssertionError(e);
  225. } catch (InvocationTargetException e) {
  226. throw new RuntimeException(e);
  227. }
  228. }
  229. @Override public byte[] getNpnSelectedProtocol(SSLSocket socket) {
  230. if (!openSslSocketClass.isInstance(socket)) {
  231. return null;
  232. }
  233. try {
  234. return (byte[]) getNpnSelectedProtocol.invoke(socket);
  235. } catch (InvocationTargetException e) {
  236. throw new RuntimeException(e);
  237. } catch (IllegalAccessException e) {
  238. throw new AssertionError(e);
  239. }
  240. }
  241. }
  242. /** OpenJDK 7 plus {@code org.mortbay.jetty.npn/npn-boot} on the boot class path. */
  243. private static class JdkWithJettyNpnPlatform extends Platform {
  244. private final Method getMethod;
  245. private final Method putMethod;
  246. private final Class<?> clientProviderClass;
  247. private final Class<?> serverProviderClass;
  248. public JdkWithJettyNpnPlatform(Method putMethod, Method getMethod, Class<?> clientProviderClass,
  249. Class<?> serverProviderClass) {
  250. this.putMethod = putMethod;
  251. this.getMethod = getMethod;
  252. this.clientProviderClass = clientProviderClass;
  253. this.serverProviderClass = serverProviderClass;
  254. }
  255. @Override public void setNpnProtocols(SSLSocket socket, byte[] npnProtocols) {
  256. try {
  257. List<String> strings = new ArrayList<String>();
  258. for (int i = 0; i < npnProtocols.length; ) {
  259. int length = npnProtocols[i++];
  260. strings.add(new String(npnProtocols, i, length, "US-ASCII"));
  261. i += length;
  262. }
  263. Object provider = Proxy.newProxyInstance(Platform.class.getClassLoader(),
  264. new Class[] {clientProviderClass, serverProviderClass},
  265. new JettyNpnProvider(strings));
  266. putMethod.invoke(null, socket, provider);
  267. } catch (UnsupportedEncodingException e) {
  268. throw new AssertionError(e);
  269. } catch (InvocationTargetException e) {
  270. throw new AssertionError(e);
  271. } catch (IllegalAccessException e) {
  272. throw new AssertionError(e);
  273. }
  274. }
  275. @Override public byte[] getNpnSelectedProtocol(SSLSocket socket) {
  276. try {
  277. JettyNpnProvider provider =
  278. (JettyNpnProvider) Proxy.getInvocationHandler(getMethod.invoke(null, socket));
  279. if (!provider.unsupported && provider.selected == null) {
  280. Logger logger = Logger.getLogger("com.squareup.okhttp.OkHttpClient");
  281. logger.log(Level.INFO,
  282. "NPN callback dropped so SPDY is disabled. " + "Is npn-boot on the boot class path?");
  283. return null;
  284. }
  285. return provider.unsupported ? null : provider.selected.getBytes("US-ASCII");
  286. } catch (UnsupportedEncodingException e) {
  287. throw new AssertionError();
  288. } catch (InvocationTargetException e) {
  289. throw new AssertionError();
  290. } catch (IllegalAccessException e) {
  291. throw new AssertionError();
  292. }
  293. }
  294. }
  295. /**
  296. * Handle the methods of NextProtoNego's ClientProvider and ServerProvider
  297. * without a compile-time dependency on those interfaces.
  298. */
  299. private static class JettyNpnProvider implements InvocationHandler {
  300. private final List<String> protocols;
  301. private boolean unsupported;
  302. private String selected;
  303. public JettyNpnProvider(List<String> protocols) {
  304. this.protocols = protocols;
  305. }
  306. @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  307. String methodName = method.getName();
  308. Class<?> returnType = method.getReturnType();
  309. if (args == null) {
  310. args = Util.EMPTY_STRING_ARRAY;
  311. }
  312. if (methodName.equals("supports") && boolean.class == returnType) {
  313. return true;
  314. } else if (methodName.equals("unsupported") && void.class == returnType) {
  315. this.unsupported = true;
  316. return null;
  317. } else if (methodName.equals("protocols") && args.length == 0) {
  318. return protocols;
  319. } else if (methodName.equals("selectProtocol")
  320. && String.class == returnType
  321. && args.length == 1
  322. && (args[0] == null || args[0] instanceof List)) {
  323. // TODO: use OpenSSL's algorithm which uses both lists
  324. List<?> serverProtocols = (List) args[0];
  325. this.selected = protocols.get(0);
  326. return selected;
  327. } else if (methodName.equals("protocolSelected") && args.length == 1) {
  328. this.selected = (String) args[0];
  329. return null;
  330. } else {
  331. return method.invoke(this, args);
  332. }
  333. }
  334. }
  335. }