【Java】プロキシ設定のチェック機能を考える ~ その2 ~

■ はじめに

https://blogs.yahoo.co.jp/dk521123/37185366.html
の続き。別のアプローチを試している。

■ 問題点に対するアプローチ

サーバに細工をし、「HTTPコード 407(プロキシ認証が必要)」を返せば
ログアウト的なことをできるらしいができればJavaアプリだけで完結したい。

問題を発生した後に、再度、Basic認証が認証NGにテストをしたところ、
期待通り、NGになったので、チェック機能を別プロセスで実行すれば
実現できそうなので、以下の関連記事を応用して実装してみる。
# 他にもっといい方法あるといいんだが...
Java から別のJava(JARファイル)を実行するには...
https://blogs.yahoo.co.jp/dk521123/37181644.html

■ 使用上の注意:Linux上での実行

 * 以下のサンプルをWindows上(Windows7/10 Java1.8.0)で確認したところ問題なかったが
   Linux(Debian8/Java1.8.0_65)上では、値の受け渡しで桁あふれを起こしていた。

 => 結論から言うと、System.exitに設定する値は『0~255』の範囲で行う必要がある

詳細は以下の関連記事を参照のこと。
https://blogs.yahoo.co.jp/dk521123/37217908.html
例:HTTPコード 407 の時
【ProxySettingChecker.java】
System.exit(407);

 ↓

【Main.java】(受け取り側)
int result = process.waitFor(); // ★ result = 145 ★

■ サンプル

https://blogs.yahoo.co.jp/dk521123/36962466.html
でプロキシ環境を構築する。以下の環境下を想定する。

 * プロキシサーバ   :192.168.211.123
 * プロキシサーバポート:3128
 * 認証ID       :admin
 * 認証パスワード   :password
SSLについて
 * 独自の証明書を設定している場合、事前に証明書をキーストアにインポートしておく必要がある
~[コマンド例]~~~
"%JAVA_HOME%\bin\keytool" -import -trustcacerts -alias java_proxy -keystore "%JAVA_HOME%\jre\lib\security\cacerts" -storepass changeit -file cacert.pem
~~~~~~~~~~

  => さもないと以下の例外が発生する
~~~~~~~~~~
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
~~~~~~~~~~
https://blogs.yahoo.co.jp/dk521123/36518468.html

ProxySettingChecker.java

別プロセスとして実行したいJava。 ProxySettingChecker.jar にする
import java.io.IOException;
import java.net.Authenticator;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.SocketTimeoutException;
import java.net.URL;

import javax.net.ssl.SSLHandshakeException;

public class ProxySettingChecker {

  private static final int TIMEOUT = 5_000;

  /**
   * Mainメソッド.
   * @param args コマンドライン引数(詳細は以下の通り)
   * 
   * <pre>
   * args[0] : 対象URL(http://xxxx) (Required)
   * args[1] : Proxy Host (Required)
   * args[2] : Proxy Port (Required)
   * args[3] : User Name (Optional)
   * args[4] : Password (Optional)
   * </pre>
   * 
   * @throws Exception 引数不正
   */
  public static void main(String[] args) throws Exception {
    if (args == null || args.length < 3) {
      throw new IllegalArgumentException("Illegal Arguments");
    }
    System.out.println("args.length : " + args.length);

    String targetUrl = args[0];
    String proxyHost = args[1];
    int proxyPort = Integer.valueOf(args[2]);

    if (args.length >= 5) {
      String userName = args[3];
      String password = args[4];
      if (userName != null && password != null) {
        System.out.println(userName + " " + password);

        // See https://qiita.com/kaakaa_hoe/items/d4fb11a3af035a287972
        System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
        Authenticator.setDefault(new Authenticator() {
          @Override
          protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(userName, password.toCharArray());
          }
        });
      }
    }

    int responseCode = ProxySettingChecker.connectUsingProxy(proxyHost, proxyPort, targetUrl, TIMEOUT);
    if (ProxySettingChecker.isValidResponse(responseCode)) {
      System.out.println("OK Setting");
      return;
    }
    System.err.println("NG Setting");
    System.exit(responseCode);
  }

  private static int connectUsingProxy(String proxyHost, int proxyPort, String url, int timeout) {
    try {
      URL targetUrl = new URL(url);
      Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
      HttpURLConnection connection = (HttpURLConnection) targetUrl.openConnection(proxy);
      connection.setConnectTimeout(timeout);
      connection.setReadTimeout(timeout);
      connection.setRequestMethod("HEAD");
      return connection.getResponseCode();
    } catch (SSLHandshakeException ex) {
      ex.printStackTrace();
      return -4;
    } catch (SocketTimeoutException ex) {
      // ex.printStackTrace();
      return -3;
    } catch (ConnectException ex) {
      // ex.printStackTrace();
      return -2;
    } catch (IOException ex) {
      // ex.printStackTrace();
      return -1;
    }
  }

  private static boolean isValidResponse(int responseCode) {
    return HttpURLConnection.HTTP_OK <= responseCode && responseCode <= 399;
  }
}

Main.java

呼び出し側
//import java.io.BufferedReader;
//import java.io.InputStreamReader;

public class Main {
  public static void main(String[] args) {
    System.out.println("Test1-1 : OK");
    try {
      execute("java", "-jar", "ProxySettingChecker.jar", "http://www.msn.com/ja-jp",
          "192.168.211.123", "3128", "admin", "password");
      Thread.sleep(1000L);
      System.out.println();
    } catch (Exception ex) {
      ex.printStackTrace();
    }

    System.out.println("Test1-2 : NG");
    try {
      execute("java", "-jar", "ProxySettingChecker.jar", "http://www.msn.com/ja-jp",
          "192.168.211.123", "3128", "admin", "dummy");
      Thread.sleep(1000L);
      System.out.println();
    } catch (Exception ex) {
      ex.printStackTrace();
    }

    System.out.println("Test1-3 : NG");
    try {
      execute("java", "-jar", "ProxySettingChecker.jar", "http://www.msn.com/ja-jp",
          "192.168.211.123", "3128", "dummy", "password");
      Thread.sleep(1000L);
      System.out.println();
    } catch (Exception ex) {
      ex.printStackTrace();
    }

    System.out.println("Test1-4 : NG");
    try {
      execute("java", "-jar", "ProxySettingChecker.jar", "http://www.msn.com/ja-jp",
          "192.168.211.123", "3128");
      Thread.sleep(1000L);
      System.out.println();
    } catch (Exception ex) {
      ex.printStackTrace();
    }

    System.out.println("Test2-1 : NG");
    try {
      execute("java", "-jar", "ProxySettingChecker.jar", "http://www.msn.com/ja-jp",
          "192.168.211.1", "3128", "admin", "password");
      Thread.sleep(1000L);
      System.out.println();
    } catch (Exception ex) {
      ex.printStackTrace();
    }

    System.out.println("Test2-2 : NG");
    try {
      execute("java", "-jar", "ProxySettingChecker.jar", "http://www.msn.com/ja-jp",
          "192.168.211.123", "3129", "admin", "password");
      Thread.sleep(1000L);
      System.out.println();
    } catch (Exception ex) {
      ex.printStackTrace();
    }

    // SSLの場合、事前に証明書をキーストアにインポートしておく必要がある
    System.out.println("Test3-1 : OK");
    try {
      execute("java", "-jar", "ProxySettingChecker.jar", "https://www.msn.com/ja-jp",
          "192.168.233.144", "3128", "admin", "password");
      Thread.sleep(1000L);
      System.out.println();
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  private static void execute(String... commands) {
    ProcessBuilder processBuilder = new ProcessBuilder(commands);

    try {
      Process process = processBuilder.start();
      int result = process.waitFor();
      System.out.println("Result Code : " + result);
      if (result != 0) {
        System.err.println("Error!");
      }
//      try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
//        String line;
//        while ((line = bufferedReader.readLine()) != null) {
//          System.out.println(line);
//        }
//      }
//      try (BufferedReader errorBufferedreader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
//        String errorLine;
//        while ((errorLine = errorBufferedreader.readLine()) != null) {
//          System.err.println(errorLine);
//        }
//      }
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}

出力結果

Test1-1 : OK
Result Code : 0

Test1-2 : NG
Result Code : 407
Error!

Test1-3 : NG
Error!
Result Code : 407

Test1-4 : NG
Error!
Result Code : 407

Test2-1 : NG
Error!
Result Code : -3


Test2-2 : NG
Result Code : -2
Error!

Test3-1 : OK
Result Code : 0

関連記事

Java】プロキシ設定のチェック機能を考える ~ その1 ~

https://blogs.yahoo.co.jp/dk521123/37185366.html

【トラブル】【JavaLinux 上で、System.exit() に設定した値が process.waitFor() で受け取った時に値が異なる

https://blogs.yahoo.co.jp/dk521123/37217908.html

Javaで、プロキシサーバ経由で接続する

https://blogs.yahoo.co.jp/dk521123/36966230.html

Javaping を考える

https://blogs.yahoo.co.jp/dk521123/37019179.html

Java で レスポンスヘッダーの日時を取得するには...

https://blogs.yahoo.co.jp/dk521123/37230186.html

Java で HTTP通信を行うには...

https://blogs.yahoo.co.jp/dk521123/37095462.html

Java から別のJava(JARファイル)を実行するには...

https://blogs.yahoo.co.jp/dk521123/37181644.html

Linux】プロキシサーバソフト [2] ~ Squid / Basic認証編 ~

https://blogs.yahoo.co.jp/dk521123/36962466.html