001/*
002 * Copyright (c) 2009 The openGion Project.
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,
013 * either express or implied. See the License for the specific language
014 * governing permissions and limitations under the License.
015 */
016package org.opengion.fukurou.util;
017
018import java.io.File;
019import java.io.IOException;
020import java.io.PrintWriter;
021import java.net.HttpURLConnection;
022import java.net.MalformedURLException;
023import java.net.URL;
024import java.util.Arrays;
025import java.util.ArrayList;
026import java.util.List;
027import java.nio.file.Files;
028import java.nio.file.Paths;
029        // import java.nio.charset.Charset;
030import java.nio.charset.StandardCharsets;
031import java.util.concurrent.TimeUnit;                                                                                   // 8.0.0.0 (2021/07/31) Add
032
033// 8.0.0.0 (2021/07/31) httpclient-4.5.5.jar → httpclient5-5.1.jar
034// org.apache.http → org.apache.hc.core5.http or org.apache.hc.client5.http
035import org.apache.hc.core5.http.Header;
036import org.apache.hc.core5.http.HttpEntity;
037import org.apache.hc.core5.http.HttpHost;
038// import org.apache.hc.core5.http.message.StatusLine;                                                  // 8.0.0.0 (2021/07/31) Delete
039import org.apache.hc.client5.http.auth.AuthScope;
040import org.apache.hc.client5.http.auth.Credentials;
041import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
042import org.apache.hc.client5.http.config.RequestConfig;
043// import org.apache.http.client.config.CookieSpecs;                                                    // 8.0.0.0 (2021/07/31) Delete
044// import org.apache.hc.client5.http.auth.CredentialsProvider;                                  // 8.0.0.0 (2021/07/31) Delete
045import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
046import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
047import org.apache.hc.client5.http.classic.methods.HttpGet;
048import org.apache.hc.client5.http.classic.methods.HttpPost;
049//import org.apache.http.client.methods.HttpUriRequest;                                                 // 8.0.0.0 (2021/07/31)
050import org.apache.hc.core5.http.ClassicHttpRequest;
051import org.apache.hc.core5.http.ContentType;
052import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
053// import org.apache.hc.client5.http.entity.mime.HttpMultipartMode;                             // 8.0.0.0 (2021/07/31) Delete
054import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
055import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
056import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
057import org.apache.hc.core5.http.message.BasicHeader;
058import org.apache.hc.core5.http.message.BasicNameValuePair;
059import org.apache.hc.core5.http.NameValuePair;
060import org.apache.hc.core5.http.io.entity.EntityUtils;
061// import org.apache.hc.client5.http.impl.classic.HttpClients;
062import org.apache.hc.client5.http.cookie.CookieStore;
063// import org.apache.hc.core5.http.io.entity.StringEntity;                                              // 8.0.0.0 (2021/07/31) Delete
064// import org.apache.http.impl.client.DefaultHttpClient;;
065import org.apache.hc.client5.http.protocol.HttpClientContext;
066// import java.io.UnsupportedEncodingException;                                                                 // 8.0.0.0 (2021/07/31) Delete
067import org.apache.hc.core5.http.ParseException;                                                                 // 8.0.0.0 (2021/07/31) Add
068
069import org.apache.hc.client5.http.cookie.BasicCookieStore;
070// import org.apache.hc.client5.http.cookie.Cookie;
071import java.net.URI;
072import java.net.URISyntaxException;
073import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
074
075// import org.apache.http.impl.client.DefaultRedirectStrategy;
076// import org.apache.http.HttpRequest;
077// import org.apache.http.HttpResponse;
078// import org.apache.http.protocol.HttpContext;
079// import org.apache.http.ProtocolException;
080// import org.apache.http.impl.client.LaxRedirectStrategy;                                              // 8.0.0.0 (2021/07/31) Delete
081
082// import org.opengion.fukurou.system.Closer;
083import org.opengion.fukurou.system.LogWriter;
084import org.opengion.fukurou.system.OgRuntimeException ;
085
086import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;
087import static org.opengion.fukurou.system.HybsConst.CR;
088
089/**
090 * HttpConnect は、指定のURL にアクセスして、データを取得します。
091 * URL へのアクセスにより、エンジンでは各種処理を実行させることが可能になります。
092 * 例えば、帳票デーモンの起動や、長時間かかる処理の実行などです。
093 * なお、URLに引数が付く場合は、ダブルコーテーションで括って下さい。
094 * URL の指定は、先頭に何もつけませ。指定の順番も関係ありません。
095 * - 付き引数は、指定順番は、関係ありません。
096 * 先頭が # の引数は、コメントと判断します。
097 *
098 * <pre>
099 * Usage: java org.opengion.fukurou.util.HttpConnect [-post=キー:ファイル名] … url [user:passwd]
100 *   args[A] : url                     URLを指定します。GETの場合、パラメータは ?KEY=VALです
101 *   args[*] : [-param=key:value]      POST/GET時のパラメータのキーと値を:で区切って指定します。(複数回指定可)
102 *   args[*] : [-header=key:value]     ヘッダーに設定するパラメータのキーと値を:で区切って指定します。(複数回指定可)
103 *   args[*] : [-auth=user:pass]       BASIC認証のエリアへのアクセス時のユーザーとパスワードを指定します
104 *   args[*] : [-proxy=host:port]      proxy を使用する場合のホストとポートを指定します。
105 *   args[*] : [-timeout=3]            接続タイムアウト時間を(秒)で指定します(初期値:無指定)
106 *   args[*] : [-encode=UTF-8]         エンコードを指定します。(初期値は UTF-8)
107 *   args[*] : [-out=ファイル名]       結果をファイルに出力します。初期値は標準出力です
108 *   args[*] : [-download=ファイル名]  ファイル名を指定して、ダウンロードします
109 *   args[*] : [-upload=ファイル名]    ファイル名を指定して、multipart/form-dataでファイルアップロードします
110 *   args[*] : [-postRedirect=true]    POST時に強制的にリダイレクトを行います(GET時は自動でリダイレクトします)(初期値:false) 7.2.5.0 (2020/06/01)
111 *   args[*] : [-errEx=true/false]     trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます(初期値:false)
112 *   args[*] : [#・・・・]                 コメント引数。(BATファイル上に残しておきたいが、使用したくない場合など)
113 *   args[*] : [-debug=true/false]     trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)
114 * </pre>
115 *
116 * ※ URLConnect との違い。
117 *    -info/-data 等の区別の廃止。(実質、-info がなくなる。)
118 *    setDownloadFile(String) 追加(-binaryの代用)
119 *    setUploadFile(String) 追加
120 *    proxy 設定の変更
121 *
122 * @og.rev 6.9.0.0 (2018/01/31) 新規作成
123 *
124 * @version  6.9.0.0
125 * @author   Kazuhiko Hasegawa
126 * @since    JDK8.0,
127 */
128public class HttpConnect {
129        /** エンコードの初期値  {@value} */
130        public static final String DEFAULT_CHARSET = "UTF-8" ;
131        /** 言語の初期値  {@value} */
132        public static final String DEFAULT_LANG = "ja-JP" ;
133        /** User-Agentの初期値  {@value} */
134        public static final String DEFAULT_AGENT = "openGion with Apache HttpClient" ;
135        /** GETで指定するときのURLの長さ制限  {@value}  (IEの場合は、2,083文字) */
136        public static final int MAX_GET_LENGTH = 2000 ;
137
138        private final String urlStr ;
139        private final String user ;
140        private final String pass ;
141
142        private int                     rpsCode         = -1;
143        private String          rpsMessage      ;
144        private String          charset         = DEFAULT_CHARSET ;
145        private String          upldFile        ;
146        private String          dwldFile        ;                               // バイナリファイルとして受け取る場合のファイル名
147        private int                     timeout         = -1;
148        private boolean         isPost          ;
149        private boolean         postRedirect ;                          // 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にリダイレクト)
150        private boolean         isDebug         ;
151
152        private HttpHost        proxy           ;
153
154        private CookieStore     ckStore         ;               // 8.0.0.0 (2021/07/31)
155
156        // 初期ヘッダー情報
157        private static final List<Header> INIT_HEADER =
158                                                        Arrays.asList(
159                                                                new BasicHeader( "Accept-Charset"       , DEFAULT_CHARSET ) ,
160                                                                new BasicHeader( "Accept-Language"      , DEFAULT_LANG  ) ,
161                                                                new BasicHeader( "User-Agent"           , DEFAULT_AGENT )
162                                                        );
163
164        private final List<NameValuePair>       reqParamList = new ArrayList<NameValuePair>();  // リクエストパラメーター(主にPOST時)
165        private final List<Header>                      headers          = new ArrayList<>( INIT_HEADER );      // ヘッダーパラメーター
166
167        // GET でのパラメータのマージ。きちんとした方法がわかるまでの暫定処置
168        private final StringBuilder                     reqParamBuf  = new StringBuilder( BUFFER_MIDDLE );
169
170        /**
171         * 接続先URLと、認証用ユーザー:パスワードを指定する、コンストラクター
172         *
173         * 認証が必要ない場合は、userPass は、null でかまいません。
174         * 接続先URLは、HttpConnect で、urlEncode しますので、そのままの文字列でかまいません。
175         *
176         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
177         * @og.rev 8.0.0.0 (2021/07/31) httpclient4 → httpclient5 対応
178         *
179         * @param       url                     接続するアドレスを指定します。(http://server:port/dir/file.html)
180         * @param       userPass        ユーザー:パスワード(認証接続が必要な場合)
181         */
182        public HttpConnect( final String url, final String userPass ) {
183                urlStr = StringUtil.urlEncode2( url );
184
185                if( StringUtil.isNull( userPass ) ) {
186                        user = null;
187                        pass = null;
188                }
189                else {
190                        final String[] prm = StringUtil.csv2Array( userPass , ':' , 2 );
191                        user = prm[0];
192                        pass = prm[1];
193                }
194        }
195
196//      /**
197//       * Form認証でのログイン
198//       *
199//       * https://hc.apache.org/httpcomponents-client-5.1.x/examples.html
200//       * https://github.com/apache/httpcomponents-client/blob/5.1.x/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientFormLogin.java
201//       *
202//       * @og.rev 8.0.0.0 (2021/07/31) httpclient4 → httpclient5 対応
203//       */
204//      private String formLogin() {
205//              String body = null;
206//              String secCheckURL = null;
207//
208//              final BasicCookieStore cookieStore = new BasicCookieStore();
209//              try (final CloseableHttpClient httpclient = HttpClients.custom()
210//                              .setDefaultCookieStore(cookieStore)
211//                              .build()) {
212////                    final HttpGet httpget = new HttpGet(urlStr);
213//                      final URI uri = new URI(urlStr);                                        // j_security_check のアドレスを取るために。
214//                      final HttpGet httpget = new HttpGet(uri);
215//
216//                      try (final CloseableHttpResponse response1 = httpclient.execute(httpget)) {
217//                              final HttpEntity entity = response1.getEntity();
218//
219//                              System.out.println("Login form get: " + response1.getCode() + " " + response1.getReasonPhrase());
220//                              final String loginBody = EntityUtils.toString( entity, charset );
221//                              // 一旦Form認証画面のHTMLが返ってくるので、その中から j_security_check を探してくる。
222//                              final int ed = loginBody.indexOf( "j_security_check" );
223//                              if( ed >= 0 ) {
224//                                      final int st = loginBody.lastIndexOf( '"',ed );         // 逆順に探す。
225//                                      if( st >= 0 ) {
226//                                              secCheckURL = uri.getScheme() + "://" + uri.getHost() + ':' + uri.getPort()
227//                                                                                      + loginBody.substring( st+1,ed ) + "j_security_check" ;
228//                                              System.out.println(secCheckURL);
229//                                      }
230//                              }
231//
232//                              EntityUtils.consume(entity);            // リソースを解放
233//
234//      //                      System.out.println("Initial set of cookies:");
235//      //                      final List<Cookie> cookies = cookieStore.getCookies();
236//      //                      if (cookies.isEmpty()) {
237//      //                              System.out.println("None");
238//      //                      } else {
239//      //                              for (int i = 0; i < cookies.size(); i++) {
240//      //                                      System.out.println("- " + cookies.get(i));
241//      //                              }
242//      //                      }
243//                      }
244//
245//                      final ClassicHttpRequest login = ClassicRequestBuilder.post()
246//      //                              .setUri(new URI("http://localhost:8828/gf/jsp/j_security_check"))
247//                                      .setUri(new URI( secCheckURL ))
248//                                      .addParameter("j_username", user)
249//                                      .addParameter("j_password", pass)
250//                                      .addParameter("j_security_check", "login")
251//                                      .build();
252//                      try (final CloseableHttpResponse response2 = httpclient.execute(login)) {
253//                              final HttpEntity entity = response2.getEntity();
254//
255//                              System.out.println("Login form get: " + response2.getCode() + " " + response2.getReasonPhrase());
256//                              body = EntityUtils.toString( entity, charset );
257//
258//                              EntityUtils.consume(entity);            // リソースを解放
259//
260//      //                      System.out.println("Post logon cookies:");
261//      //                      final List<Cookie> cookies = cookieStore.getCookies();
262//      //                      if (cookies.isEmpty()) {
263//      //                              System.out.println("None");
264//      //                      } else {
265//      //                              for (int i = 0; i < cookies.size(); i++) {
266//      //                                      System.out.println("- " + cookies.get(i));
267//      //                              }
268//      //                      }
269//                      }
270//              }
271//              catch( IOException ex ) { ex.printStackTrace(); }
272//              catch( URISyntaxException ex ) { ex.printStackTrace(); }
273//              catch( final ParseException ex ) {ex.printStackTrace();}
274//
275//              return body ;
276//      }
277
278        /**
279         * Form認証でのログイン
280         *
281         * https://hc.apache.org/httpcomponents-client-5.1.x/examples.html
282         * https://github.com/apache/httpcomponents-client/blob/5.1.x/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientFormLogin.java
283         *
284         * @og.rev 8.0.0.0 (2021/07/31) httpclient4 → httpclient5 対応
285         */
286//      private String formLogin( final CloseableHttpClient client , final URI uri ) {
287        private String formLogin( final CloseableHttpClient client , final ClassicHttpRequest method ) {
288                String body = null;
289
290                try {
291                        final URI uri = method.getUri();
292                        final URI secCheckURI = uri.resolve( "j_security_check" );
293                        if( isDebug ) { System.out.println( "secCheckURI URL=" + secCheckURI ); }
294
295                        final ClassicHttpRequest login = ClassicRequestBuilder.post()
296        //                              .setUri(new URI("http://localhost:8828/gf/jsp/j_security_check"))
297                                        .setUri( secCheckURI )
298                                        .addParameter("j_username", user)
299                                        .addParameter("j_password", pass)
300                                        .addParameter("j_security_check", "login")
301                                        .build();
302
303        // うまくいかない。
304        //              // アップロードの場合、自身のEntityを再セットしてやる。
305        //              if( !StringUtil.isNull( upldFile ) ) {
306        //                      final File file = new File( upldFile );
307        //                      if( isDebug ) { System.out.println( "  MULTI FILE=" + file ); }
308        //                      final HttpEntity entity = MultipartEntityBuilder.create()
309        //                                                                      .setCharset( StandardCharsets.UTF_8 )   // ファイル名の文字化け対策
310        //                                                                      .addBinaryBody( "upload" ,
311        //                                                                                                      file ,
312        //                                                                                                      ContentType.DEFAULT_BINARY ,
313        //                                                                                                      file.getName() )
314        //                                                                      .build();
315        //                      login.setEntity( entity );
316        //              }
317
318                        try( CloseableHttpResponse response = client.execute(login) ) {
319                                final HttpEntity entity = response.getEntity();
320
321                                rpsCode    = response.getCode();                                                                        // 8.0.0.0 (2021/07/31)
322                                rpsMessage = code2Message( rpsCode ).trim();                                            // 8.0.0.0 (2021/07/31)
323
324                                // Form認証の場合、バイナリ処理は、formLogin 内で行います。
325                                // バイナリファイルとして受け取る場合。成功(200番台)のみ処理します。
326                                if( !StringUtil.isNull( dwldFile ) && rpsCode >= 200 && rpsCode < 300 ) {
327                                        Files.write( Paths.get( dwldFile ) , EntityUtils.toByteArray( entity ) );
328                                        body = dwldFile;
329                                }
330                                else {
331                                        body = EntityUtils.toString( entity, charset );
332                                }
333                                EntityUtils.consume(entity);            // リソースを解放
334                        }
335                }
336                catch( final IOException | URISyntaxException | ParseException ex ) {
337                        throw new OgRuntimeException( ex );
338                }
339
340                return body ;
341        }
342
343        /**
344         * URL接続先のデータを取得します。
345         *
346         * この処理の前に、必要な情報を設定して置いてください。
347         * また、code や message は、このメソッドを実行しないと取得できませんのでご注意ください。
348         *
349         * 取得したデータは、指定のURL へのアクセスのみです。
350         * 通常のWebブラウザは、イメージや、JavaScriptファイル、CSSファイルなど、
351         * 各種ファイル毎にHTTP接続を行い、取得して、レンダリングします。
352         * このメソッドでの処理では、それらのファイル内に指定されているURLの
353         * 再帰的な取得は行いません。
354         * よって、フレーム処理なども行いません。
355         *
356         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
357         * @og.rev 8.0.0.0 (2021/07/31) httpclient4 → httpclient5 対応
358         *
359         * @return      接続結果
360         * @og.rtnNotNull
361         * @throws  IOException 入出力エラーが発生したとき
362         * @throws  MalformedURLException URLの形式が間違っている場合
363         */
364        public String readData() throws IOException , MalformedURLException {
365        //      HttpUriRequest method ;
366                final ClassicHttpRequest method ;                                                                                               // 8.0.0.0 (2021/07/31)
367                if( isPost ) {
368                        if( isDebug ) { System.out.println( "POST URL=" + urlStr ); }
369                        method = new HttpPost( urlStr );
370
371                        if( !reqParamList.isEmpty() ) {
372        //                      ((HttpPost)method).setEntity( new UrlEncodedFormEntity( reqParamList , DEFAULT_CHARSET ) );
373                                method.setEntity( new UrlEncodedFormEntity( reqParamList ) );                                           // 8.0.0.0 (2021/07/31)
374                                if( isDebug ) { reqParamList.forEach( v -> System.out.println( "PARAM KEY=" + v.getName() + " , VAL=" + v.getValue() ) ); }
375                        }
376
377                        if( !StringUtil.isNull( upldFile ) ) {
378                                final File file = new File( upldFile );
379                                if( isDebug ) { System.out.println( "  MULTI FILE=" + file ); }
380                                final HttpEntity entity = MultipartEntityBuilder.create()
381                //                                                              .setMode( HttpMultipartMode.BROWSER_COMPATIBLE )        // 8.0.0.0 (2021/07/31) Delete
382                                                                                .setCharset( StandardCharsets.UTF_8 )   // ファイル名の文字化け対策
383                                                                                .addBinaryBody( "upload" ,
384                                                                                                                file ,
385                                                                                                                ContentType.DEFAULT_BINARY ,
386                                                                                                                file.getName() )
387                                                                                .build();
388                //              ((HttpPost)method).setEntity( entity );
389                                method.setEntity( entity );
390                        }
391                }
392                else {
393                        // GET でのパラメータのマージ。きちんとした方法がわかるまでの暫定処置
394                        final String getStr = reqParamBuf.length() == 0
395                                                                        ? urlStr
396                                                                        : reqParamBuf.toString() ;
397
398                        if( isDebug ) { System.out.println( "GET URL=" + getStr ); }
399
400                        method = new HttpGet( getStr );
401                }
402
403                final HttpClientContext context = HttpClientContext.create();
404//              if( ckStore != null ) {                                 // 8.0.0.0 (2021/07/31) 未使用
405//                      context.setCookieStore(ckStore);
406//              }
407
408                String body = null;
409                try( CloseableHttpClient client = getClient() ;
410                         CloseableHttpResponse response = client.execute(method,context) ) {
411
412        //              final StatusLine status = response.getStatusLine();                                     // 8.0.0.0 (2021/07/31) Delete
413        //              rpsCode    = status.getStatusCode();
414        //              rpsMessage = ( code2Message( rpsCode ) + CR + status.getReasonPhrase() ).trim();
415
416                        rpsCode    = response.getCode();                                                                        // 8.0.0.0 (2021/07/31)
417                        if( isDebug ) { System.out.println( "rpsCode=" + rpsCode ); }
418                        rpsMessage = code2Message( rpsCode ).trim();                                            // 8.0.0.0 (2021/07/31)
419
420                        final HttpEntity entity = response.getEntity();
421
422                        if( entity == null ) {
423                                body = rpsMessage;                      // HttpEntity が受け取れなかった場合は、メッセージを表示します。
424                        }
425                        else {
426                                // body は一度しか処理できない。EntityUtils.toByteArray( entity ) か、EntityUtils.toString( entity, charset );
427
428                                // バイナリファイルとして受け取る場合。成功(200番台)のみ処理します。
429                                if( !StringUtil.isNull( dwldFile ) && rpsCode >= 200 && rpsCode < 300 ) {
430                                        final byte[] dwnBody = EntityUtils.toByteArray( entity );
431                                        final String text = new String( dwnBody,charset );
432
433                                        // 一旦Form認証画面のHTMLが返ってくるので、その中から j_security_check を探してくる。
434                                        if( text.contains( "j_security_check" ) ) {
435                                                body = formLogin( client,method );              // Form認証時の再接続処理
436                                        }
437                                        else {
438                                                Files.write( Paths.get( dwldFile ) , dwnBody );
439                                                body = dwldFile;
440                                        }
441                                }
442                                else {
443                                        // form認証チェックが必要なので、バイナリでも文字列で受け取る。
444                                        body = EntityUtils.toString( entity, charset );
445
446                                        // 一旦Form認証画面のHTMLが返ってくるので、その中から j_security_check を探してくる。
447                                        if( body.contains( "j_security_check" ) ) {
448                                                body = formLogin( client,method );              // Form認証時の再接続処理
449                                        }
450                                }
451                        }
452                        EntityUtils.consume(entity);                    // リソースを解放
453
454//                      // バイナリファイルとして受け取る場合。成功(200番台)のみ処理します。
455//                      if( !StringUtil.isNull( dwldFile ) && rpsCode >= 200 && rpsCode < 300 ) {
456//                              Files.write( Paths.get( dwldFile ) , EntityUtils.toByteArray( entity ) );
457//                              body = dwldFile;
458//                      }
459//                      else {
460//                              if( entity == null ) {
461//                                      body = rpsMessage;              // HttpEntity が受け取れなかった場合は、メッセージを表示します。
462//                              }
463//                              else {
464//                                      body = EntityUtils.toString( entity, charset );
465//                              }
466//                      }
467                }
468                // 8.0.0.0 (2021/07/31) Add
469                catch( final ParseException ex ) {
470                        throw new OgRuntimeException( ex );
471                }
472
473                return body;
474        }
475
476        /**
477         * 接続先の HttpClient オブジェクトを作成します。
478         *
479         * 接続に必要な情報を、設定します。
480         * CloseableHttpClient は、AutoCloseable を継承しています。
481         *
482         * 7.2.5.0 (2020/06/01)
483         *   通常、HttpClientはGETの場合は自動でリダイレクト処理しますが、
484         *   POSTの場合は、302が返るだけでリダイレクト処理しません。
485         *   http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3[HTTP RFC 2616]で規定されています。
486         *   ここでは、ダウンロードファイルがあり、POSTの場合だけ強制的に
487         *
488         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
489         * @og.rev 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にリダイレクト)
490         * @og.rev 8.0.0.0 (2021/07/31) httpclient4 → httpclient5 対応
491         *
492         * @return  HttpConnectionオブジェクト
493         * @throws  IOException 入出力エラーが発生したとき
494         */
495        private CloseableHttpClient getClient() throws MalformedURLException {
496
497                final HttpClientBuilder clBuild = HttpClientBuilder.create();
498
499                final BasicCookieStore cookieStore = new BasicCookieStore();
500                clBuild.setDefaultCookieStore(cookieStore) ;
501
502                // request configuration
503//              RequestConfig.Builde.setCookieSpec( CookieSpecs.STANDARD );                     // 最新のRFC準拠ヘッダーを理解するのが困難なので。 8.0.0.0 (2021/07/31) Delete
504
505                if( timeout >= 0 ) {
506                        final RequestConfig.Builder reqConfig = RequestConfig.custom();
507        //              reqConfig.setConnectTimeout( timeout * 1000 )                                   // timeoutの単位は(秒)、設定値は、ミリ秒
508        //                               .setSocketTimeout(  timeout * 1000 );
509                        reqConfig.setConnectTimeout( timeout ,TimeUnit.SECONDS );               // 8.0.0.0 (2021/07/31) timeoutの単位は(秒)
510
511                        clBuild.setDefaultRequestConfig( reqConfig.build() );
512                }
513
514                // 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にリダイレクト)
515                // 8.0.0.0 (2021/07/31) httpclient5-5.1対応により LaxRedirectStrategy 廃止の為一旦削除
516        //      if( postRedirect ) {
517        //              clBuild.setRedirectStrategy( new LaxRedirectStrategy() );
518        //      }
519
520                // headers (初期設定も入っているので、通常は、empty にはならない。)
521                if( !headers.isEmpty() ) {
522                        clBuild.setDefaultHeaders( headers );
523                }
524
525                // Proxy
526                if( proxy != null ) {
527                        clBuild.setProxy( proxy );
528                }
529
530                // Auth
531                // https://github.com/apache/httpcomponents-client/blob/5.1.x/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientAuthentication.java
532                if( !StringUtil.isNull( user ) ) {
533                        final URL url = new URL( urlStr );
534                        final AuthScope   scope = new AuthScope( url.getHost(), url.getPort() );
535        //              final Credentials cred  = new UsernamePasswordCredentials( user ,pass );
536                        final Credentials cred  = new UsernamePasswordCredentials( user ,pass.toCharArray() );  // 8.0.0.0 (2021/07/31)
537
538        //              final CredentialsProvider credProvider = new BasicCredentialsProvider();
539                        final BasicCredentialsProvider credProvider = new BasicCredentialsProvider();                   // 8.0.0.0 (2021/07/31)
540                        credProvider.setCredentials( scope,cred );
541                        clBuild.setDefaultCredentialsProvider( credProvider );
542                }
543
544        //      // (デフォルトのHttpClientは、最新のRFC準拠ヘッダーを理解するのが困難です。)
545        //  // RequestConfig に、CookieSpecs.STANDARD を設定しているが、効果なければ、使わなくしてしまう。
546        //      clBuild.disableCookieManagement();
547
548                return clBuild.build();         // HttpClient httpClient  = HttpClientBuilder.create().*****.build();
549        }
550
551        /**
552         * 接続先に使用する引数(パラメータ)を追加します。
553         *
554         * これは、POSTでも、GETでも使用できます。
555         * POSTの場合は、NameValuePair として、HttpPost に、Entity としてセットするデータを設定します。
556         * GET の場合は、既存の接続先URLに、&amp;キー=値・・・・ で、追記します。
557         * すでに、パラメータが指定済みの場合は、&amp; で、そうでなければ、? で連結します。
558         * ここで指定するパラメータは、内部で、urlEncode しますので、そのままの文字列でかまいません。
559         *
560         * デフォルトは、GETですが、Internet Explorer では URL に最大 2,083 文字しか指定できないため、
561         * それ以上の場合は、POST に自動で切り替えます。
562         *
563         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
564         *
565         * @param       key     パラメータキー(nullの場合は、登録しません)
566         * @param       val     パラメータ値
567         */
568        public void addRequestProperty( final String key, final String val ) {
569                if( !StringUtil.isNull( key ) ) {
570                        reqParamList.add( new BasicNameValuePair( key,val ) );                          // POST のときのパラメータ。(GETでも使えるはず?)
571
572                        if( !isPost ) {                                                                                                         // 明らかに、GET でない場合は、この処理を行わない。
573                                if( reqParamBuf.length() == 0 ) {                                                               // 初めての場合
574                                        reqParamBuf.append( urlStr )
575                                                                .append( urlStr.indexOf( '?' ) > 0 ? '&' : '?' )
576                                                                .append( StringUtil.urlEncode2( key ) )
577                                                                .append( '=' )
578                                                                .append( StringUtil.urlEncode2( val ) );                // null のときは、長さゼロ文字列になる。
579                                }
580                                else if( reqParamBuf.length() > MAX_GET_LENGTH ) {
581                                        if( isDebug ) { System.out.println( "GET → POST変更: URLの長さ制限<" + reqParamBuf.length() ); }
582                                        isPost = true;                                                                                          // GETで送れるURLの長さ制限を超えた場合は、POSTにする。
583                                }
584                                else {
585                                        reqParamBuf.append( '&' )
586                                                                .append( StringUtil.urlEncode2( key ) )
587                                                                .append( '=' )
588                                                                .append( StringUtil.urlEncode2( val ) );                // null のときは、長さゼロ文字列になる。
589                                }
590                        }
591                }
592        }
593
594        /**
595         * setRequestPropertyでセットするデータを設定します。
596         *
597         * keys,vals各々、カンマ区切りで分解します。
598         *
599         * @og.rev 5.10.16.0 (2019/10/04) 追加
600         *
601         * @param       keys    パラメータキー(カンマ区切り)
602         * @param       vals    パラメータ(カンマ区切り)
603         */
604        public void setRequestProperty( final String keys, final String vals ) {
605                if( keys != null && keys.length() > 0 && vals != null && vals.length() > 0 ){
606                        final String[]  propKeys = StringUtil.csv2Array( keys );
607                        final String[]  propVals = StringUtil.csv2Array( vals );
608
609                        if( propKeys.length == propVals.length && propKeys.length > 0 ) {
610                                for( int i=0; i<propKeys.length; i++ ) {
611                                        addRequestProperty( propKeys[i], propVals[i] );
612                                }
613                        }
614                        else {
615                                final String errMsg = "パラメータのキーと、値の数が一致しません。"   + CR
616                                                        + " key=[" + keys + "]"                                                                 + CR
617                                                        + " val=[" + vals + "]" ;
618                                throw new IllegalArgumentException( errMsg );
619                        }
620                }
621        }
622
623        /**
624         * 指定のURLに対して、コネクトするのに使用するプロキシ設定を行います。
625         * このときに、ヘッダー情報を内部変数に設定しておきます。
626         *
627         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
628         *
629         * @param       host    接続するプロキシのホスト名(nullの場合は、登録しません)
630         * @param       port    接続するプロキシのポート番号
631         */
632        public void setProxy( final String host,final int port ) {
633                if( !StringUtil.isNull( host ) ) {
634                        proxy = new HttpHost( host , port );
635                }
636        }
637
638        /**
639         * Header として、HttpClient にセットするデータを設定します。
640         *
641         * 例えばJSON形式でPOSTする場合は通常"Content-Type", "application/json"を指定します。
642         *
643         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
644         *
645         * @param       key     パラメータキー(nullの場合は、登録しません)
646         * @param       val     パラメータ値(nullの場合は、登録しません)
647         */
648        public void addHeaderProperty( final String key, final String val ) {
649                if( !StringUtil.isNull( key ) && !StringUtil.isNull( val ) ) {
650                        headers.add( new BasicHeader( key,val ) );
651                }
652        }
653
654        /**
655         * URL接続先のバイナリファイルをダウンロード取得します。
656         *
657         * 取得したファイルは、dwldFile にバイナリのまま書き込まれます。
658         * よって、エンコードの指定は不要です。
659         *
660         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
661         *
662         * @param       dwldFile ダウンロードするファイル名。
663         * @throws  IOException 入出力エラーが発生したとき
664         */
665        public void setDownloadFile( final String dwldFile ) throws IOException {
666                this.dwldFile = dwldFile;
667        }
668
669        /**
670         * URL接続先のバイナリファイルをアップロードします。
671         *
672         * 取得したファイルは、upldFile にバイナリのまま書き込まれます。
673         * よって、エンコードの指定は不要です。
674         * アップロード は、multipart/form-data で送信するため、isPost = true を
675         * 内部的に設定しておきます。
676         *
677         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
678         * @og.rev 7.2.5.0 (2020/06/01) upldFileのnull判定を入れます。
679         *
680         * @param       upldFile アップロードするファイル名。
681         * @throws  IOException 入出力エラーが発生したとき
682         */
683        public void setUploadFile( final String upldFile ) throws IOException {
684                if( upldFile != null ) {
685                        this.upldFile = upldFile;
686                        isPost = true;
687                }
688        }
689
690        /**
691         * エンコード情報を設定します。
692         *
693         * 初期値は、UTF-8 です。
694         *
695         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
696         *
697         * @param  chset エンコード情報(nullの場合は、初期値:UTF-8 になります)
698         */
699        public void setCharset( final String chset ) {
700                if( !StringUtil.isNull( chset ) ) {
701                        charset = chset;
702                }
703        }
704
705        /**
706         * 接続タイムアウト時間を(秒)で指定します
707         *
708         * 実際には、org.apache.http.client.config.RequestConfig に対して、
709         *       .setConnectTimeout( timeout * 1000 )
710         *       .setSocketTimeout(  timeout * 1000 )
711         * のように、 1000倍して設定しています。
712         * 0 は、無限のタイムアウト、マイナスは、設定しません。(つまりJavaの初期値のまま)
713         *
714         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
715         *
716         * @param       tout    タイムアウト時間(秒) (ゼロは、無制限)
717         */
718        public void setTimeout( final int tout ) {
719                timeout = tout;
720        }
721
722        /**
723         * trueの場合、POSTを使用して接続します(初期値:false)。
724         *
725         * 通常はGETですが、外部から強制的に、POSTで送信したい場合に、
726         * 設定します。
727         * ただし、バイナリファイルをアップロードか、URLの長さ制限が、
728         * {@value #MAX_GET_LENGTH} を超えた場合は、内部で自動的に、post にします。
729         *
730         * @og.rev 6.9.0.1 (2018/02/05) 新規作成
731         *
732         * @param       usePost true:POST使用/false:通常(GET)
733         */
734        public void usePost( final boolean usePost ) {
735                isPost = usePost;
736        }
737
738        /**
739         * trueの場合、POST時に強制的にリダイレクトを行います(初期値:false)。
740         *
741         * @og.rev 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にリダイレクト)
742         * @og.rev 8.0.0.0 (2021/07/31) httpclient5-5.1対応により LaxRedirectStrategy 廃止の為一旦削除
743         *
744         * @param       useRedirect     true:POST時に強制的にリダイレクト/false:通常
745         */
746        public void setPostRedirect( final boolean useRedirect ) {
747                postRedirect = useRedirect;
748        }
749
750        /**
751         * trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)。
752         *
753         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
754         *
755         * @param       isDebug true:デバッグ用のメッセージを出力/false:通常
756         */
757        public void setDebug( final boolean isDebug ) {
758                this.isDebug = isDebug;
759        }
760
761        /**
762         * 実行結果のステータスコード 情報を取得します。
763         *
764         * 結果は、#readData() メソッドをコールしないと取れません。
765         * 未実行の場合は、-1 がセットされています。
766         *
767         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
768         *
769         * @return      結果コード 情報
770         * @see         #readData()
771         */
772        public int getCode() { return rpsCode; }
773
774        /**
775         * メッセージ 情報を取得します。
776         *
777         * 結果は、#readData() メソッドをコールしないと取れません。
778         * 未実行の場合は、null がセットされています。
779         *
780         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
781         *
782         * @return      メッセージ 情報
783         */
784        public String getMessage() { return rpsMessage; }
785
786        /**
787         * HttpURLConnection のレスポンスコードに対応するメッセージ文字列を返します。
788         *
789         * HttpURLConnection の getResponseCode() メソッドにより取得された、HTTPレスポンスコード
790         * に対応する文字列を返します。この文字列は、HttpURLConnection で定義された
791         * static 定数のコメントを、定義しています。
792         *
793         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
794         *
795         * @param       code    HTTPレスポンスコード
796         *
797         * @return      レスポンスコードに対応する文字列
798         * @og.rtnNotNull
799         * @see HttpURLConnection#HTTP_ACCEPTED
800         */
801        public static String code2Message( final int code ) {
802                final String msg ;
803                switch( code ) {
804                        case 100                                                                                : msg = "100: 要求は続行可能です。"                                               ;       break;
805                        case 101                                                                                : msg = "101: プロトコルを切り替えます。"                            ;       break;
806                        case HttpURLConnection.HTTP_OK                                  : msg = "200: OK です。"                                                           ;       break;
807                        case HttpURLConnection.HTTP_CREATED                     : msg = "201: 作成されました。"                                                 ;       break;
808                        case HttpURLConnection.HTTP_ACCEPTED                    : msg = "202: 受け入れられました。"                                               ;       break;
809                        case HttpURLConnection.HTTP_NOT_AUTHORITATIVE   : msg = "203: 信頼できない情報です。"                                      ;       break;
810                        case HttpURLConnection.HTTP_NO_CONTENT                  : msg = "204: コンテンツがありません。"                                     ;       break;
811                        case HttpURLConnection.HTTP_RESET                               : msg = "205: コンテンツをリセットします。"                           ;       break;
812                        case HttpURLConnection.HTTP_PARTIAL                     : msg = "206: 部分的なコンテンツです。"                                     ;       break;
813                        case HttpURLConnection.HTTP_MULT_CHOICE                 : msg = "300: 複数の選択肢があります。"                                     ;       break;
814                        case HttpURLConnection.HTTP_MOVED_PERM                  : msg = "301: 永続的に移動されました。"                                     ;       break;
815                        case HttpURLConnection.HTTP_MOVED_TEMP                  : msg = "302: 一時的なリダイレクト。"                                      ;       break;
816                        case HttpURLConnection.HTTP_SEE_OTHER                   : msg = "303: ほかを参照してください。"                                     ;       break;
817                        case HttpURLConnection.HTTP_NOT_MODIFIED                : msg = "304: 変更されていません。"                                               ;       break;
818                        case HttpURLConnection.HTTP_USE_PROXY                   : msg = "305: プロキシを使用します。"                                      ;       break;
819                        case 306                                                                                : msg = "306: 仕様の拡張案です。"                                                ;       break;
820                        case 307                                                                                : msg = "307: 一時的なリダイレクトです。"                            ;       break;
821                        case HttpURLConnection.HTTP_BAD_REQUEST                 : msg = "400: 不当な要求です。"                                                 ;       break;
822                        case HttpURLConnection.HTTP_UNAUTHORIZED                : msg = "401: 認証されませんでした。"                                      ;       break;
823                        case HttpURLConnection.HTTP_PAYMENT_REQUIRED    : msg = "402: 支払いが必要です。"                                                ;       break;
824                        case HttpURLConnection.HTTP_FORBIDDEN                   : msg = "403: 禁止されています。"                                                ;       break;
825                        case HttpURLConnection.HTTP_NOT_FOUND                   : msg = "404: 見つかりませんでした。"                                      ;       break;
826                        case HttpURLConnection.HTTP_BAD_METHOD                  : msg = "405: メソッドは許可されません。"                            ;       break;
827                        case HttpURLConnection.HTTP_NOT_ACCEPTABLE              : msg = "406: 受け入れられません。"                                               ;       break;
828                        case HttpURLConnection.HTTP_PROXY_AUTH                  : msg = "407: プロキシの認証が必要です。"                            ;       break;
829                        case HttpURLConnection.HTTP_CLIENT_TIMEOUT              : msg = "408: 要求がタイムアウトしました。"                           ;       break;
830                        case HttpURLConnection.HTTP_CONFLICT                    : msg = "409: 重複しています。"                                                 ;       break;
831                        case HttpURLConnection.HTTP_GONE                                : msg = "410: 存在しません。"                                                  ;       break;
832                        case HttpURLConnection.HTTP_LENGTH_REQUIRED     : msg = "411: 長さが必要です。"                                                 ;       break;
833                        case HttpURLConnection.HTTP_PRECON_FAILED               : msg = "412: 前提条件が満たされていません。"                  ;       break;
834                        case HttpURLConnection.HTTP_ENTITY_TOO_LARGE    : msg = "413: 要求のエンティティが大きすぎます。"                ;       break;
835                        case HttpURLConnection.HTTP_REQ_TOO_LONG                : msg = "414: 要求のURIが大きすぎます。"                           ;       break;
836                        case HttpURLConnection.HTTP_UNSUPPORTED_TYPE    : msg = "415: サポートされないメディアタイプです。"               ;       break;
837                        case 416                                                                                : msg = "416: 要求された範囲は不十分です。"                           ;       break;
838                        case 417                                                                                : msg = "417: 要求どおりの処理が不可能です。"                  ;       break;
839                        case HttpURLConnection.HTTP_INTERNAL_ERROR              : msg = "500: 内部サーバエラーです。"                                      ;       break;
840                        case HttpURLConnection.HTTP_NOT_IMPLEMENTED     : msg = "501: 実装されていません。"                                               ;       break;
841                        case HttpURLConnection.HTTP_BAD_GATEWAY                 : msg = "502: 誤ったゲートウェイです。"                                     ;       break;
842                        case HttpURLConnection.HTTP_UNAVAILABLE                 : msg = "503: サービスが利用できません。"                            ;       break;
843                        case HttpURLConnection.HTTP_GATEWAY_TIMEOUT     : msg = "504: ゲートウェイがタイムアウトしました。"               ;       break;
844                        case HttpURLConnection.HTTP_VERSION                     : msg = "505: サポートされていないHTTPバージョンです。"   ;       break;
845                        default                                                                                 : msg = code + ": 未定義"                                                          ;       break;
846                }
847                return msg ;
848        }
849
850        /**
851         * サンプル実行用のメインメソッド
852         *
853         * <pre>
854         * Usage: java org.opengion.fukurou.util.HttpConnect [-post=キー:ファイル名] … url [user:passwd]
855         *   args[A] : url                     URLを指定します。GETの場合、パラメータは ?KEY=VALです
856         *   args[*] : [-param=key:value]      POST/GET時のパラメータのキーと値を:で区切って指定します。(複数回指定可)
857         *   args[*] : [-header=key:value]     ヘッダーに設定するパラメータのキーと値を:で区切って指定します。(複数回指定可)
858         *   args[*] : [-auth=user:pass]       BASIC認証のエリアへのアクセス時のユーザーとパスワードを指定します
859         *   args[*] : [-useForm=true/false]   認証方式にFORM認証を指定する場合、trueをセットします(初期値:false) 8.0.0.0 (2021/08/20)
860         *   args[*] : [-proxy=host:port]      proxy を使用する場合のホストとポートを指定します。
861         *   args[*] : [-timeout=3]            接続タイムアウト時間を(秒)で指定します(初期値:無指定)
862         *   args[*] : [-encode=UTF-8]         エンコードを指定します。(初期値は UTF-8)
863         *   args[*] : [-out=ファイル名]       結果をファイルに出力します。初期値は標準出力です
864         *   args[*] : [-download=ファイル名]  ファイル名を指定して、ダウンロードします
865         *   args[*] : [-upload=ファイル名]    ファイル名を指定して、multipart/form-dataでファイルアップロードします
866         *   args[*] : [-postRedirect=true]    POST時に強制的にリダイレクトを行います(GET時は自動でリダイレクトします)(初期値:false) 7.2.5.0 (2020/06/01)
867         *   args[*] : [-usePost=true]         POSTを強制的に使用する場合にセットします(初期値:false) 8.0.0.0 (2021/08/20)
868         *   args[*] : [-errEx=true/false]     trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます(初期値:false)
869         *   args[*] : [#・・・・]                 コメント引数。(BATファイル上に残しておきたいが、使用したくない場合など)
870         *   args[*] : [-debug=true/false]     trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)
871         * </pre>
872         *
873         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
874         * @og.rev 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にリダイレクト)引数を追加
875         *
876         * @param       args    コマンド引数配列
877         * @throws IOException 入出力エラーが発生したとき
878         */
879        public static void main( final String[] args ) throws IOException {
880                if( args.length < 2 ) {
881                        LogWriter.log( "Usage: java org.opengion.fukurou.util.HttpConnect [-data/-binary] … url"                                                                                                );
882                        LogWriter.log( "   args[A] : url                     URLを指定します。GETの場合、パラメータは ?KEY=VALです"                                                                       );
883                        LogWriter.log( "   args[*] : [-param=key:value]      POST/GET時のパラメータのキーと値を:で区切って指定します。(複数回指定可)"                                        );
884                        LogWriter.log( "   args[*] : [-header=key:value]     ヘッダーに設定するパラメータのキーと値を:で区切って指定します。(複数回指定可)"                                );
885                        LogWriter.log( "   args[*] : [-auth=user:pass]       BASIC認証/FORM認証のエリアへのアクセス時のユーザーとパスワードを指定します"                                             );
886//                      LogWriter.log( "   args[*] : [-useForm=true/false]   認証方式にFORM認証を指定する場合、trueをセットします(初期値:false)"                                 );
887                        LogWriter.log( "   args[*] : [-proxy=host:port]      proxy を使用する場合のホストとポートを指定します。"                                                                             );
888                        LogWriter.log( "   args[*] : [-timeout=3]            接続タイムアウト時間を(秒)で指定します(初期値:無指定)"                                                                     );
889                        LogWriter.log( "   args[*] : [-encode=UTF-8]         エンコードを指定します。(初期値は UTF-8)"                                                                                         );
890                        LogWriter.log( "   args[*] : [-out=ファイル名]           結果をファイルに出力します。初期値は標準出力です"                                                                           );
891                        LogWriter.log( "   args[*] : [-download=ファイル名]      ファイル名を指定して、ダウンロードします"                                                                                                     );
892                        LogWriter.log( "   args[*] : [-upload=ファイル名]        ファイル名を指定して、multipart/form-dataでファイルアップロードします"                                                     );
893                        LogWriter.log( "   args[*] : [-postRedirect=true]    POST時に強制的にリダイレクトを行います(GET時は自動でリダイレクトします)(初期値:false)"             );
894                        LogWriter.log( "   args[*] : [-usePost=true]         POSTを強制的に使用する場合にセットします(初期値:false)"                                                         );
895                        LogWriter.log( "   args[*] : [-errEx=true/false]     trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます(初期値:false)" );
896                        LogWriter.log( "   args[*] : [#・・・・]                 コメント引数。(BATファイル上に残しておきたいが、使用したくない場合など)"                                            );
897                        LogWriter.log( "   args[*] : [-debug=true/false]     trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)"                                               );
898                        return;
899                }
900
901                String  urlStr                  = null ;
902                final List<String> paramKey  = new ArrayList<>();                                               // パラメーターキー
903                final List<String> paramVal  = new ArrayList<>();                                               // パラメーター値
904                final List<String> headerKey = new ArrayList<>();                                               // パラメーターキー
905                final List<String> headerVal = new ArrayList<>();                                               // パラメーター値
906
907                String  userPass                = null ;                                        // -auth
908//              boolean useForm                 = false ;                                       // -useForm                     // 8.0.0.0 (2021/08/20)
909                String  proxy                   = null ;                                        // -proxy
910                int             timeout                 = -1 ;                                          // -timeout
911                String  encode                  = DEFAULT_CHARSET ;                     // -encode
912                String  outFile                 = null ;                                        // -out
913                String  dwldFile                = null ;                                        // -download
914                String  upldFile                = null ;                                        // -upload
915                boolean isEx                    = false ;                                       // -errEx
916                boolean isDebug                 = false ;                                       // -debug
917                boolean postRedirect    = false ;                                       // -postRedirect        // 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にリダイレクト)
918                boolean nonWriter               = false ;                                       // -out 指定で見つからない場合
919                boolean isPost                  = false ;                                       // -usePost
920
921//              int             code                    = -1;
922
923                for( final String arg : args ) {
924                        if( arg.startsWith( "-param=" ) ) {
925                                final String[] prm = StringUtil.csv2Array( arg.substring( "-param=".length() ) , '=' , 2 );
926                                paramKey.add( prm[0] );
927                                paramVal.add( prm[1] );
928                        }
929                        else if( arg.startsWith( "-header=" ) ) {
930                                final String[] prm = StringUtil.csv2Array( arg.substring( "-header=".length() ) , '=' , 2 );
931                                headerKey.add( prm[0] );
932                                headerVal.add( prm[1] );
933                        }
934                        else if( arg.startsWith( "-auth=" ) ) {
935                                userPass = arg.substring( "-auth=".length() );
936                                if( StringUtil.isNull( userPass ) ) {
937                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
938                                }
939                        }
940//                      else if( arg.startsWith( "-useForm=" ) ) {
941//                              useForm = "true".equalsIgnoreCase( arg.substring( "-useForm=".length() ) );
942//                      }
943                        else if( arg.startsWith( "-proxy=" ) ) {
944                                proxy = arg.substring( "-proxy=".length() );
945                                if( StringUtil.isNull( proxy ) ) {
946                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
947                                }
948                        }
949                        else if( arg.startsWith( "-timeout=" ) ) {
950                                timeout = Integer.parseInt( arg.substring( "-timeout=".length() ) );
951                        }
952                        else if( arg.startsWith( "-encode=" ) ) {
953                                encode = arg.substring( "-encode=".length() );
954                                if( StringUtil.isNull( encode ) ) {
955                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
956                                }
957                        }
958                        else if( arg.startsWith( "-out=" ) ) {
959                                outFile = arg.substring( "-out=".length() );
960                                if( StringUtil.isNull( outFile ) ) {
961                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
962                                }
963                                else {
964                                        if( "null".equalsIgnoreCase( outFile ) || "none".equalsIgnoreCase( outFile ) ) {
965                                                outFile   = null;
966                                                nonWriter = true;
967                                        }
968                                }
969                        }
970                        else if( arg.startsWith( "-download=" ) ) {
971                                dwldFile = arg.substring( "-download=".length() );
972                                if( StringUtil.isNull( dwldFile ) ) {
973                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
974                                }
975                        }
976                        else if( arg.startsWith( "-upload=" ) ) {
977                                upldFile = arg.substring( "-upload=".length() );
978                                if( StringUtil.isNull( upldFile ) ) {
979                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
980                                }
981                        }
982                        else if( arg.startsWith( "-errEx=" ) ) {
983                                isEx = "true".equalsIgnoreCase( arg.substring( "-errEx=".length() ) );
984                        }
985                        // 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にリダイレクト)
986                        else if( arg.startsWith( "-postRedirect=" ) ) {
987                                postRedirect = "true".equalsIgnoreCase( arg.substring( "-postRedirect=".length() ) );
988                        }
989                        else if( arg.startsWith( "-debug=" ) ) {
990                                isDebug = "true".equalsIgnoreCase( arg.substring( "-debug=".length() ) );
991                        }
992                        else if( arg.startsWith( "-usePost=" ) ) {
993                                isPost = "true".equalsIgnoreCase( arg.substring( "-usePost=".length() ) );
994                        }
995                        else if( StringUtil.startsChar( arg , '-' ) ) {                 // 引数が未定義(処理は継続させます。)
996                                System.err.println( "Error Argment:" + arg );
997                        }
998                        else if( StringUtil.startsChar( arg , '#' ) ) {                 // 引数がコメント
999                                continue;
1000                        }
1001                        else {
1002                                urlStr = arg;
1003                        }
1004                }
1005
1006                try {                                                                   // try catch を入れます。
1007                        final HttpConnect conn = new HttpConnect( urlStr,userPass );
1008                        conn.usePost( isPost );
1009                        conn.setDebug( isDebug );                       // 最初に入れておけば、それ以降、有効になります。
1010
1011                        for( int i=0; i<paramKey.size(); i++ ) {
1012                                conn.addRequestProperty( paramKey.get(i) , paramVal.get(i) );
1013                        }
1014
1015                        for( int i=0; i<headerKey.size(); i++ ) {
1016                                conn.addHeaderProperty( headerKey.get(i) , headerVal.get(i) );
1017                        }
1018
1019                        // 6.8.1.3 (2017/08/04) proxy の設定
1020                        if( !StringUtil.isNull( proxy ) ) {
1021                                final String[] prm = StringUtil.csv2Array( proxy , ':' , 2 );
1022                                final String host = prm[0];
1023                                final int    port = Integer.parseInt( prm[1] );
1024                                conn.setProxy( host , port );
1025                        }
1026
1027                        conn.setCharset(                encode );               // encode 指定
1028                        conn.setTimeout(                timeout );              // timeout属性追加
1029                        conn.setUploadFile(             upldFile );
1030                        conn.setDownloadFile(   dwldFile );
1031                        conn.setPostRedirect(   postRedirect ); // 7.2.5.0 (2020/06/01)
1032
1033                        final String outData = conn.readData();                 // 8.0.0.0 (2021/08/20) テスト用
1034
1035                        try( PrintWriter writer = StringUtil.isNull( outFile )
1036                                                                                                ? FileUtil.getLogWriter( "System.out" )
1037                                                                                                : FileUtil.getPrintWriter( new File( outFile ),encode ) ) {
1038                                if( !nonWriter ) {
1039                                        writer.println( outData );
1040                                }
1041                                final int code = conn.getCode();
1042
1043                                // isEx=trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます
1044                                if( code >= 400 ) {
1045                                        final String errMsg = conn.getMessage();
1046                                        writer.println( errMsg );
1047                                        if( isEx ) {
1048                                                throw new OgRuntimeException( errMsg );
1049                                        }
1050                                        else {
1051                                                System.exit( code );
1052                                        }
1053                                }
1054                        }
1055                }
1056                catch( final Throwable th ) {
1057                        throw new OgRuntimeException( th );
1058                }
1059        }
1060}