001/* 002 * Copyright (C) 2015-2018 Neo Visionaries Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package NeoVisionaries.WebSockets; 017 018 019import java.io.IOException; 020import java.net.Socket; 021import java.net.URI; 022import java.net.URISyntaxException; 023import java.net.URL; 024import javax.net.SocketFactory; 025import javax.net.ssl.SSLContext; 026import javax.net.ssl.SSLSocketFactory; 027 028 029/** 030 * Factory to create {@link WebSocket} instances. 031 * 032 * <EMBED CLASS='external-html' DATA-FILE-ID=LICENSE> 033 */ 034public class WebSocketFactory 035{ 036 private final SocketFactorySettings mSocketFactorySettings; 037 private final ProxySettings mProxySettings; 038 private int mConnectionTimeout; 039 private int mSocketTimeout; 040 private DualStackMode mDualStackMode = DualStackMode.BOTH; 041 private int mDualStackFallbackDelay = 250; 042 private boolean mVerifyHostname = true; 043 private String[] mServerNames; 044 045 046 /** 047 * Constructor. 048 */ 049 public WebSocketFactory() 050 { 051 mSocketFactorySettings = new SocketFactorySettings(); 052 mProxySettings = new ProxySettings(this); 053 } 054 055 056 /** 057 * Copy constructor. 058 * 059 * @param other 060 * A {@code WebSocketFactory} instance to copy. 061 * 062 * @throws IllegalArgumentException 063 * If the given {@code WebSocketFactory} instance is null. 064 * 065 * @since 2.10 066 */ 067 public WebSocketFactory(WebSocketFactory other) 068 { 069 if (other == null) 070 { 071 throw new IllegalArgumentException("The given WebSocketFactory is null"); 072 } 073 074 mSocketFactorySettings = new SocketFactorySettings(other.mSocketFactorySettings); 075 mProxySettings = new ProxySettings(this, other.mProxySettings); 076 mConnectionTimeout = other.mConnectionTimeout; 077 mSocketTimeout = other.mSocketTimeout; 078 mDualStackMode = other.mDualStackMode; 079 mDualStackFallbackDelay = other.mDualStackFallbackDelay; 080 mVerifyHostname = other.mVerifyHostname; 081 082 if (other.mServerNames != null) 083 { 084 mServerNames = new String[other.mServerNames.length]; 085 System.arraycopy(other.mServerNames, 0, mServerNames, 0, mServerNames.length); 086 } 087 } 088 089 090 /** 091 * Get the socket factory that has been set by {@link 092 * #setSocketFactory(SocketFactory)}. 093 * 094 * @return 095 * The socket factory. 096 */ 097 public SocketFactory getSocketFactory() 098 { 099 return mSocketFactorySettings.getSocketFactory(); 100 } 101 102 103 /** 104 * Set a socket factory. 105 * See {@link #createSocket(URI)} for details. 106 * 107 * @param factory 108 * A socket factory. 109 * 110 * @return 111 * {@code this} instance. 112 */ 113 public WebSocketFactory setSocketFactory(SocketFactory factory) 114 { 115 mSocketFactorySettings.setSocketFactory(factory); 116 117 return this; 118 } 119 120 121 /** 122 * Get the SSL socket factory that has been set by {@link 123 * #setSSLSocketFactory(SSLSocketFactory)}. 124 * 125 * @return 126 * The SSL socket factory. 127 */ 128 public SSLSocketFactory getSSLSocketFactory() 129 { 130 return mSocketFactorySettings.getSSLSocketFactory(); 131 } 132 133 134 /** 135 * Set an SSL socket factory. 136 * See {@link #createSocket(URI)} for details. 137 * 138 * @param factory 139 * An SSL socket factory. 140 * 141 * @return 142 * {@code this} instance. 143 */ 144 public WebSocketFactory setSSLSocketFactory(SSLSocketFactory factory) 145 { 146 mSocketFactorySettings.setSSLSocketFactory(factory); 147 148 return this; 149 } 150 151 152 /** 153 * Get the SSL context that has been set by {@link #setSSLContext(SSLContext)}. 154 * 155 * @return 156 * The SSL context. 157 */ 158 public SSLContext getSSLContext() 159 { 160 return mSocketFactorySettings.getSSLContext(); 161 } 162 163 164 /** 165 * Set an SSL context to get a socket factory. 166 * See {@link #createSocket(URI)} for details. 167 * 168 * @param context 169 * An SSL context. 170 * 171 * @return 172 * {@code this} instance. 173 */ 174 public WebSocketFactory setSSLContext(SSLContext context) 175 { 176 mSocketFactorySettings.setSSLContext(context); 177 178 return this; 179 } 180 181 182 /** 183 * Get the proxy settings. 184 * 185 * @return 186 * The proxy settings. 187 * 188 * @since 1.3 189 * 190 * @see ProxySettings 191 */ 192 public ProxySettings getProxySettings() 193 { 194 return mProxySettings; 195 } 196 197 198 /** 199 * Get the timeout value in milliseconds for socket connection. 200 * The default value is 0 and it means an infinite timeout. 201 * 202 * <p> 203 * When a {@code createSocket} method which does not have {@code 204 * timeout} argument is called, the value returned by this method 205 * is used as a timeout value for socket connection. 206 * </p> 207 * 208 * @return 209 * The connection timeout value in milliseconds. 210 * 211 * @since 1.10 212 */ 213 public int getConnectionTimeout() 214 { 215 return mConnectionTimeout; 216 } 217 218 219 /** 220 * Set the timeout value in milliseconds for socket connection. 221 * A timeout of zero is interpreted as an infinite timeout. 222 * 223 * @param timeout 224 * The connection timeout value in milliseconds. 225 * 226 * @return 227 * {@code this} object. 228 * 229 * @throws IllegalArgumentException 230 * The given timeout value is negative. 231 * 232 * @since 1.10 233 */ 234 public WebSocketFactory setConnectionTimeout(int timeout) 235 { 236 if (timeout < 0) 237 { 238 throw new IllegalArgumentException("timeout value cannot be negative."); 239 } 240 241 mConnectionTimeout = timeout; 242 243 return this; 244 } 245 246 247 /** 248 * Get the timeout value in milliseconds for socket read and write operations. 249 * The default value is 0 and it means an infinite timeout. 250 * 251 * <p> 252 * This can be changed later with {@code getSocket().setSoTimeout(int)}. 253 * </p> 254 * 255 * @return 256 * The socket timeout value in milliseconds. 257 * 258 * @see Socket#setSoTimeout(int) 259 * 260 * @since 2.14 261 */ 262 public int getSocketTimeout() 263 { 264 return mSocketTimeout; 265 } 266 267 268 /** 269 * Set the timeout value in milliseconds for socket read and write operations. 270 * A timeout of zero is interpreted as an infinite timeout. 271 * 272 * <p> 273 * This can be changed later with {@code getSocket().setSoTimeout(int)}. 274 * </p> 275 * 276 * @param timeout 277 * The socket timeout value in milliseconds. 278 * 279 * @return 280 * {@code this} object. 281 * 282 * @throws IllegalArgumentException 283 * The given timeout value is negative. 284 * 285 * @see Socket#setSoTimeout(int) 286 * 287 * @since 2.14 288 */ 289 public WebSocketFactory setSocketTimeout(int timeout) 290 { 291 if (timeout < 0) 292 { 293 throw new IllegalArgumentException("timeout value cannot be negative."); 294 } 295 296 mSocketTimeout = timeout; 297 298 return this; 299 } 300 301 302 /** 303 * Get the dual stack mode that will be applied when establishing a socket 304 * connection. The default value is {@link DualStackMode#BOTH}. 305 * 306 * <p> 307 * A hostname may resolve to an arbitrary amount of IPv4 and IPv6 addresses. 308 * This controls which IP address families will be used when establishing 309 * a connection. Note that IPv6 will be preferred, if activated. 310 * </p> 311 * 312 * @return 313 * The dual stack mode. 314 */ 315 public DualStackMode getDualStackMode() 316 { 317 return mDualStackMode; 318 } 319 320 321 /** 322 * Set the dual stack mode that will be applied when establishing a socket 323 * connection. 324 * 325 * @param mode 326 * The dual stack mode to be applied. 327 * 328 * @return 329 * {@code this} object. 330 */ 331 public WebSocketFactory setDualStackMode(DualStackMode mode) 332 { 333 mDualStackMode = mode; 334 335 return this; 336 } 337 338 339 /** 340 * Get the dual stack fallback delay in milliseconds that will be applied 341 * when establishing a socket connection. 342 * 343 * <p> 344 * A hostname may resolve to an arbitrary amount of IPv4 and IPv6 addresses. 345 * This controls the maximum amount of time that may pass between attempts 346 * to establish a socket connection to an IP addresses before trying the 347 * next one. Note that the previous attempt will not be aborted. The 348 * connections will race until one has been established. 349 * </p> 350 * 351 * @return 352 * The dual stack fallback delay in milliseconds. 353 */ 354 public int getDualStackFallbackDelay() 355 { 356 return mDualStackFallbackDelay; 357 } 358 359 360 /** 361 * Set the dual stack fallback delay in milliseconds that will be applied 362 * when establishing a socket connection. 363 * 364 * @param delay 365 * The dual stack fallback delay in milliseconds. 366 * 367 * @return 368 * {@code this} object. 369 */ 370 public WebSocketFactory setDualStackFallbackDelay(int delay) 371 { 372 373 if (delay < 0) 374 { 375 throw new IllegalArgumentException("delay value cannot be negative."); 376 } 377 378 mDualStackFallbackDelay = delay; 379 380 return this; 381 } 382 383 384 /** 385 * Get the flag which indicates whether the hostname in the 386 * server's certificate should be verified or not. The default 387 * value is {@code true}. See the description of {@link 388 * #setVerifyHostname(boolean)} to understand what this boolean 389 * flag means. 390 * 391 * @return 392 * {@code true} if hostname verification is enabled. 393 * 394 * @since 2.3 395 */ 396 public boolean getVerifyHostname() 397 { 398 return mVerifyHostname; 399 } 400 401 402 /** 403 * Set the flag which indicates whether the hostname in the 404 * server's certificate should be verified or not. The default 405 * value is {@code true}. 406 * 407 * <p> 408 * Manual hostname verification has been enabled since the version 409 * 2.1. Because the verification is executed manually after {@code 410 * Socket.}{@link java.net.Socket#connect(java.net.SocketAddress, int) 411 * connect(SocketAddress, int)} 412 * succeeds, the hostname verification is always executed even if 413 * you has passed an {@link SSLContext} which naively accepts any 414 * server certificate (e.g. <code><a href= 415 * "https://gist.github.com/TakahikoKawasaki/d07de2218b4b81bf65ac" 416 * >NaiveSSLContext</a></code>). However, this behavior is not 417 * desirable in some cases and you may want to disable the hostname 418 * verification. This setter method exists for the purpose and you 419 * can disable hostname verification by passing {@code false} to 420 * this method. 421 * </p> 422 * 423 * @param verifyHostname 424 * {@code true} to enable hostname verification. 425 * {@code false} to disable hostname verification. 426 * 427 * @return 428 * {@code this} object. 429 * 430 * @since 2.3 431 */ 432 public WebSocketFactory setVerifyHostname(boolean verifyHostname) 433 { 434 mVerifyHostname = verifyHostname; 435 436 return this; 437 } 438 439 440 /** 441 * Get server names for SNI (Server Name Indication). 442 * 443 * @return 444 * List of host names. 445 * 446 * @since 2.4 447 */ 448 public String[] getServerNames() 449 { 450 return mServerNames; 451 } 452 453 454 /** 455 * Set server names for SNI (Server Name Indication). 456 * 457 * If {@code setServerNames(List<SNIServerName>)} method of 458 * {@link javax.net.ssl.SSLParameters SSLParameters} class is available 459 * in the underlying system, the method is called to set up server names 460 * for SNI (Server Name Indication). 461 * 462 * @param serverNames 463 * List of host names. 464 * 465 * @return 466 * {@code this} object. 467 * 468 * @since 2.4 469 */ 470 public WebSocketFactory setServerNames(String[] serverNames) 471 { 472 mServerNames = serverNames; 473 474 return this; 475 } 476 477 478 /** 479 * Set a server name for SNI (Server Name Indication). 480 * 481 * This method internally creates a String array of size 1 which 482 * contains the given {@code serverName} and calls {@link 483 * #setServerNames(String[])}. 484 * 485 * @param serverName 486 * A host name. 487 * 488 * @return 489 * {@code this} object. 490 * 491 * @since 2.4 492 */ 493 public WebSocketFactory setServerName(String serverName) 494 { 495 return setServerNames(new String[] { serverName }); 496 } 497 498 499 /** 500 * Create a WebSocket. 501 * 502 * <p> 503 * This method is an alias of {@link #createSocket(String, int) 504 * createSocket}{@code (uri, }{@link #getConnectionTimeout()}{@code )}. 505 * </p> 506 * 507 * @param uri 508 * The URI of the WebSocket endpoint on the server side. 509 * 510 * @return 511 * A WebSocket. 512 * 513 * @throws IllegalArgumentException 514 * The given URI is {@code null} or violates RFC 2396. 515 * 516 * @throws IOException 517 * Failed to create a socket. Or, HTTP proxy handshake or SSL 518 * handshake failed. 519 */ 520 public WebSocket createSocket(String uri) throws IOException 521 { 522 return createSocket(uri, getConnectionTimeout()); 523 } 524 525 526 /** 527 * Create a WebSocket. 528 * 529 * <p> 530 * This method is an alias of {@link #createSocket(URI, int) createSocket}{@code 531 * (}{@link URI#create(String) URI.create}{@code (uri), timeout)}. 532 * </p> 533 * 534 * @param uri 535 * The URI of the WebSocket endpoint on the server side. 536 * 537 * @param timeout 538 * The timeout value in milliseconds for socket connection. 539 * A timeout of zero is interpreted as an infinite timeout. 540 * 541 * @return 542 * A WebSocket. 543 * 544 * @throws IllegalArgumentException 545 * The given URI is {@code null} or violates RFC 2396, or 546 * the given timeout value is negative. 547 * 548 * @throws IOException 549 * Failed to create a socket. Or, HTTP proxy handshake or SSL 550 * handshake failed. 551 * 552 * @since 1.10 553 */ 554 public WebSocket createSocket(String uri, int timeout) throws IOException 555 { 556 if (uri == null) 557 { 558 throw new IllegalArgumentException("The given URI is null."); 559 } 560 561 if (timeout < 0) 562 { 563 throw new IllegalArgumentException("The given timeout value is negative."); 564 } 565 566 return createSocket(URI.create(uri), timeout); 567 } 568 569 570 /** 571 * Create a WebSocket. 572 * 573 * <p> 574 * This method is an alias of {@link #createSocket(URL, int) createSocket}{@code 575 * (url, }{@link #getConnectionTimeout()}{@code )}. 576 * </p> 577 * 578 * @param url 579 * The URL of the WebSocket endpoint on the server side. 580 * 581 * @return 582 * A WebSocket. 583 * 584 * @throws IllegalArgumentException 585 * The given URL is {@code null} or failed to be converted into a URI. 586 * 587 * @throws IOException 588 * Failed to create a socket. Or, HTTP proxy handshake or SSL 589 * handshake failed. 590 */ 591 public WebSocket createSocket(URL url) throws IOException 592 { 593 return createSocket(url, getConnectionTimeout()); 594 } 595 596 597 /** 598 * Create a WebSocket. 599 * 600 * <p> 601 * This method is an alias of {@link #createSocket(URI, int) createSocket}{@code 602 * (url.}{@link URL#toURI() toURI()}{@code , timeout)}. 603 * </p> 604 * 605 * @param url 606 * The URL of the WebSocket endpoint on the server side. 607 * 608 * @param timeout 609 * The timeout value in milliseconds for socket connection. 610 * 611 * @return 612 * A WebSocket. 613 * 614 * @throws IllegalArgumentException 615 * The given URL is {@code null} or failed to be converted into a URI, 616 * or the given timeout value is negative. 617 * 618 * @throws IOException 619 * Failed to create a socket. Or, HTTP proxy handshake or SSL 620 * handshake failed. 621 * 622 * @since 1.10 623 */ 624 public WebSocket createSocket(URL url, int timeout) throws IOException 625 { 626 if (url == null) 627 { 628 throw new IllegalArgumentException("The given URL is null."); 629 } 630 631 if (timeout < 0) 632 { 633 throw new IllegalArgumentException("The given timeout value is negative."); 634 } 635 636 try 637 { 638 return createSocket(url.toURI(), timeout); 639 } 640 catch (URISyntaxException e) 641 { 642 throw new IllegalArgumentException("Failed to convert the given URL into a URI."); 643 } 644 } 645 646 647 /** 648 * Create a WebSocket. This method is an alias of {@link #createSocket(URI, int) 649 * createSocket}{@code (uri, }{@link #getConnectionTimeout()}{@code )}. 650 * 651 * <p> 652 * A socket factory (= a {@link SocketFactory} instance) to create a raw 653 * socket (= a {@link Socket} instance) is determined as described below. 654 * </p> 655 * 656 * <ol> 657 * <li> 658 * If the scheme of the URI is either {@code wss} or {@code https}, 659 * <ol type="i"> 660 * <li> 661 * If an {@link SSLContext} instance has been set by {@link 662 * #setSSLContext(SSLContext)}, the value returned from {@link 663 * SSLContext#getSocketFactory()} method of the instance is used. 664 * </li> 665 * <li> 666 * Otherwise, if an {@link SSLSocketFactory} instance has been 667 * set by {@link #setSSLSocketFactory(SSLSocketFactory)}, the 668 * instance is used. 669 * </li> 670 * <li> 671 * Otherwise, the value returned from {@link SSLSocketFactory#getDefault()} 672 * is used. 673 * </li> 674 * </ol> 675 * </li> 676 * <li> 677 * Otherwise (= the scheme of the URI is either {@code ws} or {@code http}), 678 * <ol type="i"> 679 * <li> 680 * If a {@link SocketFactory} instance has been set by {@link 681 * #setSocketFactory(SocketFactory)}, the instance is used. 682 * </li> 683 * <li> 684 * Otherwise, the value returned from {@link SocketFactory#getDefault()} 685 * is used. 686 * </li> 687 * </ol> 688 * </li> 689 * </ol> 690 * 691 * @param uri 692 * The URI of the WebSocket endpoint on the server side. 693 * The scheme part of the URI must be one of {@code ws}, 694 * {@code wss}, {@code http} and {@code https} 695 * (case-insensitive). 696 * 697 * @return 698 * A WebSocket. 699 * 700 * @throws IllegalArgumentException 701 * The given URI is {@code null} or violates RFC 2396. 702 * 703 * @throws IOException 704 * Failed to create a socket. 705 */ 706 public WebSocket createSocket(URI uri) throws IOException 707 { 708 return createSocket(uri, getConnectionTimeout()); 709 } 710 711 712 /** 713 * Create a WebSocket. 714 * 715 * <p> 716 * A socket factory (= a {@link SocketFactory} instance) to create a raw 717 * socket (= a {@link Socket} instance) is determined as described below. 718 * </p> 719 * 720 * <ol> 721 * <li> 722 * If the scheme of the URI is either {@code wss} or {@code https}, 723 * <ol type="i"> 724 * <li> 725 * If an {@link SSLContext} instance has been set by {@link 726 * #setSSLContext(SSLContext)}, the value returned from {@link 727 * SSLContext#getSocketFactory()} method of the instance is used. 728 * </li> 729 * <li> 730 * Otherwise, if an {@link SSLSocketFactory} instance has been 731 * set by {@link #setSSLSocketFactory(SSLSocketFactory)}, the 732 * instance is used. 733 * </li> 734 * <li> 735 * Otherwise, the value returned from {@link SSLSocketFactory#getDefault()} 736 * is used. 737 * </li> 738 * </ol> 739 * </li> 740 * <li> 741 * Otherwise (= the scheme of the URI is either {@code ws} or {@code http}), 742 * <ol type="i"> 743 * <li> 744 * If a {@link SocketFactory} instance has been set by {@link 745 * #setSocketFactory(SocketFactory)}, the instance is used. 746 * </li> 747 * <li> 748 * Otherwise, the value returned from {@link SocketFactory#getDefault()} 749 * is used. 750 * </li> 751 * </ol> 752 * </li> 753 * </ol> 754 * 755 * @param uri 756 * The URI of the WebSocket endpoint on the server side. 757 * The scheme part of the URI must be one of {@code ws}, 758 * {@code wss}, {@code http} and {@code https} 759 * (case-insensitive). 760 * 761 * @param timeout 762 * The timeout value in milliseconds for socket connection. 763 * 764 * @return 765 * A WebSocket. 766 * 767 * @throws IllegalArgumentException 768 * The given URI is {@code null} or violates RFC 2396, or 769 * the given timeout value is negative. 770 * 771 * @throws IOException 772 * Failed to create a socket. 773 * 774 * @since 1.10 775 */ 776 public WebSocket createSocket(URI uri, int timeout) throws IOException 777 { 778 if (uri == null) 779 { 780 throw new IllegalArgumentException("The given URI is null."); 781 } 782 783 if (timeout < 0) 784 { 785 throw new IllegalArgumentException("The given timeout value is negative."); 786 } 787 788 // Split the URI. 789 String scheme = uri.getScheme(); 790 String userInfo = uri.getUserInfo(); 791 String host = Misc.extractHost(uri); 792 int port = uri.getPort(); 793 String path = uri.getRawPath(); 794 String query = uri.getRawQuery(); 795 796 return createSocket(scheme, userInfo, host, port, path, query, timeout); 797 } 798 799 800 private WebSocket createSocket( 801 String scheme, String userInfo, String host, int port, 802 String path, String query, int timeout) throws IOException 803 { 804 // True if 'scheme' is 'wss' or 'https'. 805 boolean secure = isSecureConnectionRequired(scheme); 806 807 // Check if 'host' is specified. 808 if (host == null || host.length() == 0) 809 { 810 throw new IllegalArgumentException("The host part is empty."); 811 } 812 813 // Determine the path. 814 path = determinePath(path); 815 816 // Create a Socket instance and a connector to connect to the server. 817 SocketConnector connector = createRawSocket(host, port, secure, timeout); 818 819 // Create a WebSocket instance. 820 return createWebSocket(secure, userInfo, host, port, path, query, connector); 821 } 822 823 824 private static boolean isSecureConnectionRequired(String scheme) 825 { 826 if (scheme == null || scheme.length() == 0) 827 { 828 throw new IllegalArgumentException("The scheme part is empty."); 829 } 830 831 if ("wss".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme)) 832 { 833 return true; 834 } 835 836 if ("ws".equalsIgnoreCase(scheme) || "http".equalsIgnoreCase(scheme)) 837 { 838 return false; 839 } 840 841 throw new IllegalArgumentException("Bad scheme: " + scheme); 842 } 843 844 845 private static String determinePath(String path) 846 { 847 if (path == null || path.length() == 0) 848 { 849 return "/"; 850 } 851 852 if (path.startsWith("/")) 853 { 854 return path; 855 } 856 else 857 { 858 return "/" + path; 859 } 860 } 861 862 863 private SocketConnector createRawSocket( 864 String host, int port, boolean secure, int timeout) throws IOException 865 { 866 // Determine the port number. Especially, if 'port' is -1, 867 // it is converted to 80 or 443. 868 port = determinePort(port, secure); 869 870 // True if a proxy server should be used. 871 boolean proxied = (mProxySettings.getHost() != null); 872 873 // See "Figure 2 -- Proxy server traversal decision tree" at 874 // http://www.infoq.com/articles/Web-Sockets-Proxy-Servers 875 876 if (proxied) 877 { 878 // Create a connector to connect to the proxy server. 879 return createProxiedRawSocket(host, port, secure, timeout); 880 } 881 else 882 { 883 // Create a connector to connect to the WebSocket endpoint directly. 884 return createDirectRawSocket(host, port, secure, timeout); 885 } 886 } 887 888 889 private SocketConnector createProxiedRawSocket( 890 String host, int port, boolean secure, int timeout) 891 { 892 // Determine the port number of the proxy server. 893 // Especially, if getPort() returns -1, the value 894 // is converted to 80 or 443. 895 int proxyPort = determinePort(mProxySettings.getPort(), mProxySettings.isSecure()); 896 897 // Select a socket factory. 898 SocketFactory factory = mProxySettings.selectSocketFactory(); 899 900 // The address to connect to. 901 Address address = new Address(mProxySettings.getHost(), proxyPort); 902 903 // The delegatee for the handshake with the proxy. 904 ProxyHandshaker handshaker = new ProxyHandshaker(host, port, mProxySettings); 905 906 // SSLSocketFactory for SSL handshake with the WebSocket endpoint. 907 SSLSocketFactory sslSocketFactory = secure ? 908 (SSLSocketFactory)mSocketFactorySettings.selectSocketFactory(secure) : null; 909 910 // Create an instance that will execute the task to connect to the server later. 911 return new SocketConnector( 912 factory, address, timeout, mSocketTimeout, mProxySettings.getServerNames(), handshaker, 913 sslSocketFactory, host, port) 914 .setDualStackSettings(mDualStackMode, mDualStackFallbackDelay) 915 .setVerifyHostname(mVerifyHostname); 916 } 917 918 919 private SocketConnector createDirectRawSocket(String host, int port, boolean secure, int timeout) 920 { 921 // Select a socket factory. 922 SocketFactory factory = mSocketFactorySettings.selectSocketFactory(secure); 923 924 // The address to connect to. 925 Address address = new Address(host, port); 926 927 // Create an instance that will execute the task to connect to the server later. 928 return new SocketConnector(factory, address, timeout, mServerNames, mSocketTimeout) 929 .setDualStackSettings(mDualStackMode, mDualStackFallbackDelay) 930 .setVerifyHostname(mVerifyHostname); 931 } 932 933 934 private static int determinePort(int port, boolean secure) 935 { 936 if (0 <= port) 937 { 938 return port; 939 } 940 941 if (secure) 942 { 943 return 443; 944 } 945 else 946 { 947 return 80; 948 } 949 } 950 951 952 private WebSocket createWebSocket( 953 boolean secure, String userInfo, String host, int port, 954 String path, String query, SocketConnector connector) 955 { 956 // The value for "Host" HTTP header. 957 if (0 <= port) 958 { 959 host = host + ":" + port; 960 } 961 962 // The value for Request-URI of Request-Line. 963 if (query != null) 964 { 965 path = path + "?" + query; 966 } 967 968 return new WebSocket(this, secure, userInfo, host, path, connector); 969 } 970}