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.net.URI; 020import java.net.URISyntaxException; 021import java.net.URL; 022import java.util.ArrayList; 023import java.util.List; 024import java.util.Map; 025import java.util.TreeMap; 026import javax.net.SocketFactory; 027import javax.net.ssl.SSLContext; 028import javax.net.ssl.SSLSocketFactory; 029 030 031/** 032 * Proxy settings. 033 * 034 * <EMBED CLASS='external-html' DATA-FILE-ID=LICENSE><BR /> 035 * 036 * <p> 037 * If a proxy server's host name is set (= if {@link #getHost()} 038 * returns a non-null value), a socket factory that creates a 039 * socket to communicate with the proxy server is selected based 040 * on the settings of this {@code ProxySettings} instance. The 041 * following is the concrete flow to select a socket factory. 042 * </p> 043 * 044 * <blockquote> 045 * <ol> 046 * <li> 047 * If {@link #isSecure()} returns {@code true}, 048 * <ol type="i"> 049 * <li> 050 * If an {@link SSLContext} instance has been set by {@link 051 * #setSSLContext(SSLContext)}, the value returned from {@link 052 * SSLContext#getSocketFactory()} method of the instance is used. 053 * </li> 054 * <li> 055 * Otherwise, if an {@link SSLSocketFactory} instance has been 056 * set by {@link #setSSLSocketFactory(SSLSocketFactory)}, the 057 * instance is used. 058 * </li> 059 * <li> 060 * Otherwise, the value returned from {@link SSLSocketFactory#getDefault()} 061 * is used. 062 * </li> 063 * </ol> 064 * </li> 065 * <li> 066 * Otherwise (= {@link #isSecure()} returns {@code false}), 067 * <ol type="i"> 068 * <li> 069 * If a {@link SocketFactory} instance has been set by {@link 070 * #setSocketFactory(SocketFactory)}, the instance is used. 071 * </li> 072 * <li> 073 * Otherwise, the value returned from {@link SocketFactory#getDefault()} 074 * is used. 075 * </li> 076 * </ol> 077 * </li> 078 * </ol> 079 * </blockquote> 080 * 081 * <p> 082 * Note that the current implementation supports only Basic Authentication 083 * for authentication at the proxy server. 084 * </p> 085 * 086 * @see WebSocketFactory#getProxySettings() 087 * 088 * @since 1.3 089 */ 090public class ProxySettings 091{ 092 private final WebSocketFactory mWebSocketFactory; 093 private final Map<String, List<String>> mHeaders; 094 private final SocketFactorySettings mSocketFactorySettings; 095 private boolean mSecure; 096 private String mHost; 097 private int mPort; 098 private String mId; 099 private String mPassword; 100 private String[] mServerNames; 101 102 103 /** 104 * Constructor. 105 * A {@code WebSocketFactory} instance to be associated with. 106 * 107 * @param factory factory 108 */ 109 ProxySettings(WebSocketFactory factory) 110 { 111 mWebSocketFactory = factory; 112 mHeaders = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER); 113 mSocketFactorySettings = new SocketFactorySettings(); 114 115 reset(); 116 } 117 118 119 /** 120 * Constructor with settings. 121 * 122 * @param factory 123 * A {@code WebSocketFactory} instance to be associated with. 124 * 125 * @param settings 126 * Settings to copy. 127 * 128 * @since 2.10 129 */ 130 ProxySettings(WebSocketFactory factory, ProxySettings settings) 131 { 132 this(factory); 133 134 mHeaders.putAll(settings.mHeaders); 135 mSecure = settings.mSecure; 136 mHost = settings.mHost; 137 mPort = settings.mPort; 138 mId = settings.mId; 139 mPassword = settings.mPassword; 140 141 if (settings.mServerNames != null) 142 { 143 mServerNames = new String[settings.mServerNames.length]; 144 System.arraycopy(settings.mServerNames, 0, mServerNames, 0, mServerNames.length); 145 } 146 } 147 148 149 /** 150 * Get the associated {@link WebSocketFactory} instance. 151 */ 152 public WebSocketFactory getWebSocketFactory() 153 { 154 return mWebSocketFactory; 155 } 156 157 158 /** 159 * Reset the proxy settings. To be concrete, parameter values are 160 * set as shown below. 161 * 162 * <blockquote> 163 * <table border="1" cellpadding="5" style="border-collapse: collapse;"> 164 * <thead> 165 * <tr> 166 * <th>Name</th> 167 * <th>Value</th> 168 * <th>Description</th> 169 * </tr> 170 * </thead> 171 * <tbody> 172 * <tr> 173 * <td>Secure</td> 174 * <td>{@code false}</td> 175 * <td>Use TLS to connect to the proxy server or not.</td> 176 * </tr> 177 * <tr> 178 * <td>Host</td> 179 * <td>{@code null}</td> 180 * <td>The host name of the proxy server.</td> 181 * </tr> 182 * <tr> 183 * <td>Port</td> 184 * <td>{@code -1}</td> 185 * <td>The port number of the proxy server.</td> 186 * </tr> 187 * <tr> 188 * <td>ID</td> 189 * <td>{@code null}</td> 190 * <td>The ID for authentication at the proxy server.</td> 191 * </tr> 192 * <tr> 193 * <td>Password</td> 194 * <td>{@code null}</td> 195 * <td>The password for authentication at the proxy server.</td> 196 * </tr> 197 * <tr> 198 * <td>Headers</td> 199 * <td>Cleared</td> 200 * <td>Additional HTTP headers passed to the proxy server.</td> 201 * </tr> 202 * <tr> 203 * <td>Server Names</td> 204 * <td>{@code null}</td> 205 * <td>Server names for SNI (Server Name Indication).</td> 206 * </tr> 207 * </tbody> 208 * </table> 209 * </blockquote> 210 * 211 * @return 212 * {@code this} object. 213 */ 214 public ProxySettings reset() 215 { 216 mSecure = false; 217 mHost = null; 218 mPort = -1; 219 mId = null; 220 mPassword = null; 221 mHeaders.clear(); 222 mServerNames = null; 223 224 return this; 225 } 226 227 228 /** 229 * Check whether use of TLS is enabled or disabled. 230 * 231 * @return 232 * {@code true} if TLS is used in the communication with 233 * the proxy server. 234 */ 235 public boolean isSecure() 236 { 237 return mSecure; 238 } 239 240 241 /** 242 * Enable or disable use of TLS. 243 * 244 * @param secure 245 * {@code true} to use TLS in the communication with 246 * the proxy server. 247 * 248 * @return 249 * {@code this} object. 250 */ 251 public ProxySettings setSecure(boolean secure) 252 { 253 mSecure = secure; 254 255 return this; 256 } 257 258 259 /** 260 * Get the host name of the proxy server. 261 * 262 * <p> 263 * The default value is {@code null}. If this method returns 264 * a non-null value, it is used as the proxy server. 265 * </p> 266 * 267 * @return 268 * The host name of the proxy server. 269 */ 270 public String getHost() 271 { 272 return mHost; 273 } 274 275 276 /** 277 * Set the host name of the proxy server. 278 * 279 * <p> 280 * If a non-null value is set, it is used as the proxy server. 281 * </p> 282 * 283 * @param host 284 * The host name of the proxy server. 285 * 286 * @return 287 * {@code this} object. 288 */ 289 public ProxySettings setHost(String host) 290 { 291 mHost = host; 292 293 return this; 294 } 295 296 297 /** 298 * Get the port number of the proxy server. 299 * 300 * <p> 301 * The default value is {@code -1}. {@code -1} means that 302 * the default port number ({@code 80} for non-secure 303 * connections and {@code 443} for secure connections) 304 * should be used. 305 * </p> 306 * 307 * @return 308 * The port number of the proxy server. 309 */ 310 public int getPort() 311 { 312 return mPort; 313 } 314 315 316 /** 317 * Set the port number of the proxy server. 318 * 319 * <p> 320 * If {@code -1} is set, the default port number ({@code 80} 321 * for non-secure connections and {@code 443} for secure 322 * connections) is used. 323 * </p> 324 * 325 * @param port 326 * The port number of the proxy server. 327 * 328 * @return 329 * {@code this} object. 330 */ 331 public ProxySettings setPort(int port) 332 { 333 mPort = port; 334 335 return this; 336 } 337 338 339 /** 340 * Get the ID for authentication at the proxy server. 341 * 342 * <p> 343 * The default value is {@code null}. If this method returns 344 * a non-null value, it is used as the ID for authentication 345 * at the proxy server. To be concrete, the value is used to 346 * generate the value of <code><a href= 347 * "http://tools.ietf.org/html/rfc2616#section-14.34" 348 * >Proxy-Authorization</a></code> header. 349 * </p> 350 * 351 * @return 352 * The ID for authentication at the proxy server. 353 */ 354 public String getId() 355 { 356 return mId; 357 } 358 359 360 /** 361 * Set the ID for authentication at the proxy server. 362 * 363 * <p> 364 * If a non-null value is set, it is used as the ID for 365 * authentication at the proxy server. To be concrete, the 366 * value is used to generate the value of <code><a href= 367 * "http://tools.ietf.org/html/rfc2616#section-14.34" 368 * >Proxy-Authorization</a></code> header. 369 * </p> 370 * 371 * @param id 372 * The ID for authentication at the proxy server. 373 * 374 * @return 375 * {@code this} object. 376 */ 377 public ProxySettings setId(String id) 378 { 379 mId = id; 380 381 return this; 382 } 383 384 385 /** 386 * Get the password for authentication at the proxy server. 387 * 388 * @return 389 * The password for authentication at the proxy server. 390 */ 391 public String getPassword() 392 { 393 return mPassword; 394 } 395 396 397 /** 398 * Set the password for authentication at the proxy server. 399 * 400 * @param password 401 * The password for authentication at the proxy server. 402 * 403 * @return 404 * {@code this} object. 405 */ 406 public ProxySettings setPassword(String password) 407 { 408 mPassword = password; 409 410 return this; 411 } 412 413 414 /** 415 * Set credentials for authentication at the proxy server. 416 * This method is an alias of {@link #setId(String) setId}{@code 417 * (id).}{@link #setPassword(String) setPassword}{@code 418 * (password)}. 419 * 420 * @param id 421 * The ID. 422 * 423 * @param password 424 * The password. 425 * 426 * @return 427 * {@code this} object. 428 */ 429 public ProxySettings setCredentials(String id, String password) 430 { 431 return setId(id).setPassword(password); 432 } 433 434 435 /** 436 * Set the proxy server by a URI. See the description of 437 * {@link #setServer(URI)} about how the parameters are updated. 438 * 439 * @param uri 440 * The URI of the proxy server. If {@code null} is given, 441 * none of the parameters are updated. 442 * 443 * @return 444 * {@code this} object. 445 * 446 * @throws IllegalArgumentException 447 * Failed to convert the given string to a {@link URI} instance. 448 */ 449 public ProxySettings setServer(String uri) 450 { 451 if (uri == null) 452 { 453 return this; 454 } 455 456 return setServer(URI.create(uri)); 457 } 458 459 460 /** 461 * Set the proxy server by a URL. See the description of 462 * {@link #setServer(URI)} about how the parameters are updated. 463 * 464 * @param url 465 * The URL of the proxy server. If {@code null} is given, 466 * none of the parameters are updated. 467 * 468 * @return 469 * {@code this} object. 470 * 471 * @throws IllegalArgumentException 472 * Failed to convert the given URL to a {@link URI} instance. 473 */ 474 public ProxySettings setServer(URL url) 475 { 476 if (url == null) 477 { 478 return this; 479 } 480 481 try 482 { 483 return setServer(url.toURI()); 484 } 485 catch (URISyntaxException e) 486 { 487 throw new IllegalArgumentException(e); 488 } 489 } 490 491 492 /** 493 * Set the proxy server by a URI. The parameters are updated as 494 * described below. 495 * 496 * <blockquote> 497 * <dl> 498 * <dt>Secure</dt> 499 * <dd><p> 500 * If the URI contains the scheme part and its value is 501 * either {@code "http"} or {@code "https"} (case-insensitive), 502 * the {@code secure} parameter is updated to {@code false} 503 * or to {@code true} accordingly. In other cases, the parameter 504 * is not updated. 505 * </p></dd> 506 * <dt>ID & Password</dt> 507 * <dd><p> 508 * If the URI contains the userinfo part and the ID embedded 509 * in the userinfo part is not an empty string, the {@code 510 * id} parameter and the {@code password} parameter are updated 511 * accordingly. In other cases, the parameters are not updated. 512 * </p></dd> 513 * <dt>Host</dt> 514 * <dd><p> 515 * The {@code host} parameter is always updated by the given URI. 516 * </p></dd> 517 * <dt>Port</dt> 518 * <dd><p> 519 * The {@code port} parameter is always updated by the given URI. 520 * </p></dd> 521 * </dl> 522 * </blockquote> 523 * 524 * @param uri 525 * The URI of the proxy server. If {@code null} is given, 526 * none of the parameters is updated. 527 * 528 * @return 529 * {@code this} object. 530 */ 531 public ProxySettings setServer(URI uri) 532 { 533 if (uri == null) 534 { 535 return this; 536 } 537 538 String scheme = uri.getScheme(); 539 String userInfo = uri.getUserInfo(); 540 String host = uri.getHost(); 541 int port = uri.getPort(); 542 543 return setServer(scheme, userInfo, host, port); 544 } 545 546 547 private ProxySettings setServer(String scheme, String userInfo, String host, int port) 548 { 549 setByScheme(scheme); 550 setByUserInfo(userInfo); 551 mHost = host; 552 mPort = port; 553 554 return this; 555 } 556 557 558 private void setByScheme(String scheme) 559 { 560 if ("http".equalsIgnoreCase(scheme)) 561 { 562 mSecure = false; 563 } 564 else if ("https".equalsIgnoreCase(scheme)) 565 { 566 mSecure = true; 567 } 568 } 569 570 571 private void setByUserInfo(String userInfo) 572 { 573 if (userInfo == null) 574 { 575 return; 576 } 577 578 String[] pair = userInfo.split(":", 2); 579 String id; 580 String pw; 581 582 switch (pair.length) 583 { 584 case 2: 585 id = pair[0]; 586 pw = pair[1]; 587 break; 588 589 case 1: 590 id = pair[0]; 591 pw = null; 592 break; 593 594 default: 595 return; 596 } 597 598 if (id.length() == 0) 599 { 600 return; 601 } 602 603 mId = id; 604 mPassword = pw; 605 } 606 607 608 /** 609 * Get additional HTTP headers passed to the proxy server. 610 * 611 * @return 612 * Additional HTTP headers passed to the proxy server. 613 * The comparator of the returned map is {@link 614 * String#CASE_INSENSITIVE_ORDER}. 615 */ 616 public Map<String, List<String>> getHeaders() 617 { 618 return mHeaders; 619 } 620 621 622 /** 623 * Add an additional HTTP header passed to the proxy server. 624 * 625 * @param name 626 * The name of an HTTP header (case-insensitive). 627 * If {@code null} or an empty string is given, 628 * nothing is added. 629 * 630 * @param value 631 * The value of the HTTP header. 632 * 633 * @return 634 * {@code this} object. 635 */ 636 public ProxySettings addHeader(String name, String value) 637 { 638 if (name == null || name.length() == 0) 639 { 640 return this; 641 } 642 643 List<String> list = mHeaders.get(name); 644 645 if (list == null) 646 { 647 list = new ArrayList<String>(); 648 mHeaders.put(name, list); 649 } 650 651 list.add(value); 652 653 return this; 654 } 655 656 657 /** 658 * Get the socket factory that has been set by {@link 659 * #setSocketFactory(SocketFactory)}. 660 * 661 * @return 662 * The socket factory. 663 */ 664 public SocketFactory getSocketFactory() 665 { 666 return mSocketFactorySettings.getSocketFactory(); 667 } 668 669 670 /** 671 * Set a socket factory. 672 * 673 * @param factory 674 * A socket factory. 675 * 676 * @return 677 * {@code this} instance. 678 */ 679 public ProxySettings setSocketFactory(SocketFactory factory) 680 { 681 mSocketFactorySettings.setSocketFactory(factory); 682 683 return this; 684 } 685 686 687 /** 688 * Get the SSL socket factory that has been set by {@link 689 * #setSSLSocketFactory(SSLSocketFactory)}. 690 * 691 * @return 692 * The SSL socket factory. 693 */ 694 public SSLSocketFactory getSSLSocketFactory() 695 { 696 return mSocketFactorySettings.getSSLSocketFactory(); 697 } 698 699 700 /** 701 * Set an SSL socket factory. 702 * 703 * @param factory 704 * An SSL socket factory. 705 * 706 * @return 707 * {@code this} instance. 708 */ 709 public ProxySettings setSSLSocketFactory(SSLSocketFactory factory) 710 { 711 mSocketFactorySettings.setSSLSocketFactory(factory); 712 713 return this; 714 } 715 716 717 /** 718 * Get the SSL context that has been set by {@link #setSSLContext(SSLContext)}. 719 * 720 * @return 721 * The SSL context. 722 */ 723 public SSLContext getSSLContext() 724 { 725 return mSocketFactorySettings.getSSLContext(); 726 } 727 728 729 /** 730 * Set an SSL context to get a socket factory. 731 * 732 * @param context 733 * An SSL context. 734 * 735 * @return 736 * {@code this} instance. 737 */ 738 public ProxySettings setSSLContext(SSLContext context) 739 { 740 mSocketFactorySettings.setSSLContext(context); 741 742 return this; 743 } 744 745 746 SocketFactory selectSocketFactory() 747 { 748 return mSocketFactorySettings.selectSocketFactory(mSecure); 749 } 750 751 752 /** 753 * Get server names for SNI (Server Name Indication). 754 * 755 * @return 756 * List of host names. 757 * 758 * @since 2.4 759 */ 760 public String[] getServerNames() 761 { 762 return mServerNames; 763 } 764 765 766 /** 767 * Set server names for SNI (Server Name Indication). 768 * 769 * If {@code setServerNames(List<SNIServerName>)} method of 770 * {@link javax.net.ssl.SSLParameters SSLParameters} class is available 771 * in the underlying system, the method is called to set up server names 772 * for SNI (Server Name Indication). 773 * 774 * @param serverNames 775 * List of host names. 776 * 777 * @return 778 * {@code this} object. 779 * 780 * @since 2.4 781 */ 782 public ProxySettings setServerNames(String[] serverNames) 783 { 784 mServerNames = serverNames; 785 786 return this; 787 } 788 789 790 /** 791 * Set a server name for SNI (Server Name Indication). 792 * 793 * This method internally creates a String array of size 1 which 794 * contains the given {@code serverName} and calls {@link 795 * #setServerNames(String[])}. 796 * 797 * @param serverName 798 * A host name. 799 * 800 * @return 801 * {@code this} object. 802 * 803 * @since 2.4 804 */ 805 public ProxySettings setServerName(String serverName) 806 { 807 return setServerNames(new String[] { serverName }); 808 } 809}