    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;
    031
    032import org.apache.http.Header;
    033import org.apache.http.HttpEntity;
    034import org.apache.http.HttpHost;
    035import org.apache.http.StatusLine;
    036import org.apache.http.auth.AuthScope;
    037import org.apache.http.auth.Credentials;
    038import org.apache.http.auth.UsernamePasswordCredentials;
    039import org.apache.http.client.config.RequestConfig;
    040import org.apache.http.client.config.CookieSpecs;
    041import org.apache.http.client.CredentialsProvider;
    042import org.apache.http.client.entity.UrlEncodedFormEntity;
    043import org.apache.http.client.methods.CloseableHttpResponse;
    044import org.apache.http.client.methods.HttpGet;
    045import org.apache.http.client.methods.HttpPost;
    046import org.apache.http.client.methods.HttpUriRequest;
    047import org.apache.http.entity.ContentType;
    048import org.apache.http.entity.mime.MultipartEntityBuilder;
    049import org.apache.http.entity.mime.HttpMultipartMode;
    050import org.apache.http.impl.client.BasicCredentialsProvider;
    051import org.apache.http.impl.client.CloseableHttpClient;
    052import org.apache.http.impl.client.HttpClientBuilder;
    053import org.apache.http.message.BasicHeader;
    054import org.apache.http.message.BasicNameValuePair;
    055import org.apache.http.NameValuePair;
    056import org.apache.http.util.EntityUtils;
    057
    058// import org.apache.http.impl.client.DefaultRedirectStrategy;
    059// import org.apache.http.HttpRequest;
    060// import org.apache.http.HttpResponse;
    061// import org.apache.http.protocol.HttpContext;
    062// import org.apache.http.ProtocolException;
    063import org.apache.http.impl.client.LaxRedirectStrategy;
    064
    065// import org.opengion.fukurou.system.Closer;
    066import org.opengion.fukurou.system.LogWriter;
    067import org.opengion.fukurou.system.OgRuntimeException ;
    068
    069import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;
    070import static org.opengion.fukurou.system.HybsConst.CR;
    071
    072/**
    073 * HttpConnect は、指定のURL にアクセスして、データを取得します。
    074 * URL へのアクセスにより、エンジンでは各種処理を実行させることが可能になります。
    075 * 例えば、帳票デーモンの起動や、長時間かかる処理の実行などです。
    076 * なお、URLに引数が付く場合は、ダブルコーテーションで括って下さい。
    077 * URL の指定は、先頭に何もつけませ。指定の順番も関係ありません。
    078 * - 付き引数は、指定順番は、関係ありません。
    079 * 先頭が # の引数は、コメントと判断します。
    080 *
    081 * <pre>
    082 * Usage: java org.opengion.fukurou.util.HttpConnect [-post=キー:ファイル名] … url [user:passwd]
    083 *   args[A] : url                     ＵＲＬを指定します。GETの場合、パラメータは ?KEY=VALです
    084 *   args[*] : [-param=key:value]      POST/GET時のﾊﾟﾗﾒｰﾀのｷｰと値を:で区切って指定します。(複数回指定可)
    085 *   args[*] : [-header=key:value]     ﾍｯﾀﾞｰに設定するﾊﾟﾗﾒｰﾀのｷｰと値を:で区切って指定します。(複数回指定可)
    086 *   args[*] : [-auth=user:pass]       BASIC認証のエリアへのアクセス時のユーザーとパスワードを指定します
    087 *   args[*] : [-proxy=host:port]      proxy を使用する場合のホストとポートを指定します。
    088 *   args[*] : [-timeout=3]            接続タイムアウト時間を(秒)で指定します(初期値:無指定)
    089 *   args[*] : [-encode=UTF-8]         エンコードを指定します。(初期値は UTF-8)
    090 *   args[*] : [-out=ファイル名]       結果をファイルに出力します。初期値は標準出力です
    091 *   args[*] : [-download=ファイル名]  ﾌｧｲﾙ名を指定して、ﾀﾞｳﾝﾛｰﾄﾞします
    092 *   args[*] : [-upload=ファイル名]    ﾌｧｲﾙ名を指定して、multipart/form-dataでﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞします
    093 *   args[*] : [-postRedirect=true]    POST時に強制的にﾘﾀﾞｲﾚｸﾄを行います(GET時は自動でﾘﾀﾞｲﾚｸﾄします)(初期値:false) 7.2.5.0 (2020/06/01)
    094 *   args[*] : [-errEx=true/false]     trueの場合、ﾚｽﾎﾟﾝｽｺｰﾄﾞが、4XX,5XX の時に RuntimeException を投げます(初期値:false)
    095 *   args[*] : [#････]                 ｺﾒﾝﾄ引数。(BATﾌｧｲﾙ上に残しておきたいが、使用したくない場合など)
    096 *   args[*] : [-debug=true/false]     trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)
    097 * </pre>
    098 *
    099 * ※ URLConnect との違い。
    100 *    -info/-data 等の区別の廃止。(実質、-info がなくなる。)
    101 *    setDownloadFile(String) 追加(-binaryの代用)
    102 *    setUploadFile(String) 追加
    103 *    proxy 設定の変更
    104 *
    105 * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    106 *
    107 * @version  6.9.0.0
    108 * @author   Kazuhiko Hasegawa
    109 * @since    JDK8.0,
    110 */
    111public class HttpConnect {
    112        /** エンコードの初期値  {@value} */
    113        public static final String DEFAULT_CHARSET = "UTF-8" ;
    114        /** 言語の初期値  {@value} */
    115        public static final String DEFAULT_LANG = "ja-JP" ;
    116        /** User-Agentの初期値  {@value} */
    117        public static final String DEFAULT_AGENT = "openGion with Apache HttpClient" ;
    118        /** GETで指定するときのURLの長さ制限  {@value}  (IEの場合は、2,083文字) */
    119        public static final int MAX_GET_LENGTH = 2000 ;
    120
    121        private final String urlStr ;
    122        private final String user ;
    123        private final String pass ;
    124
    125        private int                     rpsCode         = -1;
    126        private String          rpsMessage      ;
    127        private String          charset         = DEFAULT_CHARSET ;
    128        private String          upldFile        ;
    129        private String          dwldFile        ;                               // ﾊﾞｲﾅﾘﾌｧｲﾙとして受け取る場合のファイル名
    130        private int                     timeout         = -1;
    131        private boolean         isPost          ;
    132        private boolean         postRedirect ;                          // 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にﾘﾀﾞｲﾚｸﾄ)
    133        private boolean         isDebug         ;
    134
    135        private HttpHost        proxy           ;
    136
    137        // 初期ﾍｯﾀﾞｰ情報
    138        private static final List<Header> INIT_HEADER =
    139                                                                                        Arrays.asList(
    140                                                                                                new BasicHeader( "Accept-Charset"       , DEFAULT_CHARSET ) ,
    141                                                                                                new BasicHeader( "Accept-Language"      , DEFAULT_LANG  ) ,
    142                                                                                                new BasicHeader( "User-Agent"           , DEFAULT_AGENT )
    143                                                                                        );
    144
    145        private final List<NameValuePair>       reqParamList = new ArrayList<NameValuePair>();  // ﾘｸｴｽﾄﾊﾟﾗﾒｰﾀｰ(主にPOST時)
    146        private final List<Header>                      headers          = new ArrayList<>( INIT_HEADER );      // ﾍｯﾀﾞｰﾊﾟﾗﾒｰﾀｰ
    147
    148        // GET でのﾊﾟﾗﾒｰﾀのマージ。きちんとした方法がわかるまでの暫定処置
    149        private final StringBuilder                     reqParamBuf  = new StringBuilder( BUFFER_MIDDLE );
    150
    151        /**
    152         * 接続先URLと、認証用ユーザー:パスワードを指定する、コンストラクター
    153         *
    154         * 認証が必要ない場合は、userPass は、null でかまいません。
    155         * 接続先URLは、HttpConnect で、urlEncode しますので、そのままの文字列でかまいません。
    156         *
    157         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    158         *
    159         * @param       url                     接続するアドレスを指定します。(http://server:port/dir/file.html)
    160         * @param       userPass        ユーザー：パスワード(認証接続が必要な場合)
    161         */
    162        public HttpConnect( final String url, final String userPass ) {
    163                urlStr = StringUtil.urlEncode2( url );
    164
    165                if( StringUtil.isNull( userPass ) ) {
    166                        user = null;
    167                        pass = null;
    168                }
    169                else {
    170                        final String[] prm = StringUtil.csv2Array( userPass , ':' , 2 );
    171                        user = prm[0];
    172                        pass = prm[1];
    173                }
    174        }
    175
    176        /**
    177         * URL接続先のデータを取得します。
    178         *
    179         * この処理の前に、必要な情報を設定して置いてください。
    180         * また、code や message は、このメソッドを実行しないと取得できませんのでご注意ください。
    181         *
    182         * 取得したデータは、指定のURL へのアクセスのみです。
    183         * 通常のWebブラウザは、イメージや、JavaScriptファイル、CSSファイルなど、
    184         * 各種ファイル毎にHTTP接続を行い、取得して、レンダリングします。
    185         * このメソッドでの処理では、それらのファイル内に指定されているURLの
    186         * 再帰的な取得は行いません。
    187         * よって、フレーム処理なども行いません。
    188         *
    189         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    190         *
    191         * @return      接続結果
    192         * @og.rtnNotNull
    193         * @throws  IOException 入出力エラーが発生したとき
    194         * @throws  MalformedURLException URLの形式が間違っている場合
    195         */
    196        public String readData() throws IOException , MalformedURLException {
    197                HttpUriRequest method ;
    198                if( isPost ) {
    199                        if( isDebug ) { System.out.println( "POST URL=" + urlStr ); }
    200                        method = new HttpPost( urlStr );
    201
    202                        if( !reqParamList.isEmpty() ) {
    203                                ((HttpPost)method).setEntity( new UrlEncodedFormEntity( reqParamList , DEFAULT_CHARSET ) );
    204                                if( isDebug ) { reqParamList.forEach( v -> System.out.println( "PARAM KEY=" + v.getName() + " , VAL=" + v.getValue() ) ); }
    205                        }
    206
    207                        if( !StringUtil.isNull( upldFile ) ) {
    208                                final File file = new File( upldFile );
    209                                if( isDebug ) { System.out.println( "  MULTI FILE=" + file ); }
    210                                final HttpEntity entity = MultipartEntityBuilder.create()
    211                                                                                .setMode( HttpMultipartMode.BROWSER_COMPATIBLE )
    212                                                                                .setCharset( StandardCharsets.UTF_8 )   // ﾌｧｲﾙ名の文字化け対策
    213                                                                                .addBinaryBody( "upload" ,
    214                                                                                                                file ,
    215                                                                                                                ContentType.DEFAULT_BINARY ,
    216                                                                                                                file.getName() )
    217                                                                                .build();
    218                                ((HttpPost)method).setEntity( entity );
    219                        }
    220                }
    221                else {
    222                        // GET でのﾊﾟﾗﾒｰﾀのマージ。きちんとした方法がわかるまでの暫定処置
    223                        final String getStr = reqParamBuf.length() == 0
    224                                                                        ? urlStr
    225                                                                        : reqParamBuf.toString() ;
    226
    227                        if( isDebug ) { System.out.println( "GET URL=" + getStr ); }
    228
    229                        method = new HttpGet( getStr );
    230                }
    231
    232                String body = null;
    233                try( CloseableHttpClient client = getClient() ;
    234                         CloseableHttpResponse response = client.execute(method) ) {
    235
    236                        final StatusLine status = response.getStatusLine();
    237                        final HttpEntity entity = response.getEntity();
    238
    239                        rpsCode    = status.getStatusCode();
    240                        rpsMessage = ( code2Message( rpsCode ) + CR + status.getReasonPhrase() ).trim();
    241
    242                        // バイナリファイルとして受け取る場合。成功(200番台)のみ処理します。
    243                        if( !StringUtil.isNull( dwldFile ) && rpsCode >= 200 && rpsCode < 300 ) {
    244                                Files.write( Paths.get( dwldFile ) , EntityUtils.toByteArray( entity ) );
    245                                body = dwldFile;
    246                        }
    247                        else {
    248                                if( entity == null ) {
    249                                        body = rpsMessage;              // HttpEntity が受け取れなかった場合は、メッセージを表示します。
    250                                }
    251                                else {
    252                                        body = EntityUtils.toString( entity, charset );
    253                                }
    254                        }
    255                }
    256
    257                return body;
    258        }
    259
    260        /**
    261         * 接続先の HttpClient オブジェクトを作成します。
    262         *
    263         * 接続に必要な情報を、設定します。
    264         * CloseableHttpClient は、AutoCloseable を継承しています。
    265         *
    266         * 7.2.5.0 (2020/06/01)
    267         *   通常、HttpClientはGETの場合は自動でﾘﾀﾞｲﾚｸﾄ処理しますが、
    268         *   POSTの場合は、302が返るだけでﾘﾀﾞｲﾚｸﾄ処理しません。
    269         *   http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3[HTTP RFC 2616]で規定されています。
    270         *   ここでは、ﾀﾞｳﾝﾛｰﾄﾞﾌｧｲﾙがあり、POSTの場合だけ強制的に
    271         *
    272         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    273         * @og.rev 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にﾘﾀﾞｲﾚｸﾄ)
    274         *
    275         * @return  HttpConnectionオブジェクト
    276         * @throws  IOException 入出力エラーが発生したとき
    277         */
    278        private CloseableHttpClient getClient() throws MalformedURLException {
    279
    280                final HttpClientBuilder clBuild = HttpClientBuilder.create();
    281
    282                // request configuration
    283                final RequestConfig.Builder reqConfig = RequestConfig.custom()
    284                        .setCookieSpec( CookieSpecs.STANDARD );                                 // 最新のRFC準拠ヘッダーを理解するのが困難なので。
    285
    286                if( timeout >= 0 ) {
    287                        reqConfig.setConnectTimeout( timeout * 1000 )                   // timeoutの単位は(秒)、設定値は、ミリ秒
    288                                         .setSocketTimeout(  timeout * 1000 );
    289                }
    290
    291                clBuild.setDefaultRequestConfig( reqConfig.build() );
    292
    293                // 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にﾘﾀﾞｲﾚｸﾄ)
    294                if( postRedirect ) {
    295                        clBuild.setRedirectStrategy( new LaxRedirectStrategy() );
    296                }
    297
    298                // headers (初期設定も入っているので、通常は、empty にはならない。)
    299                if( !headers.isEmpty() ) {
    300                        clBuild.setDefaultHeaders( headers );
    301                }
    302
    303                // Auth
    304                if( !StringUtil.isNull( user ) ) {
    305                        final URL url = new URL( urlStr );
    306                        final AuthScope   scope = new AuthScope( url.getHost(), url.getPort() );
    307                        final Credentials cred  = new UsernamePasswordCredentials( user ,pass );
    308
    309                        final CredentialsProvider credProvider = new BasicCredentialsProvider();
    310                        credProvider.setCredentials( scope,cred );
    311
    312                        clBuild.setDefaultCredentialsProvider( credProvider );
    313                }
    314
    315                // Proxy
    316                if( proxy != null ) {
    317                        clBuild.setProxy( proxy );
    318                }
    319
    320        //      // (デフォルトのHttpClientは、最新のRFC準拠ヘッダーを理解するのが困難です。)
    321        //  // RequestConfig に、CookieSpecs.STANDARD を設定しているが、効果なければ、使わなくしてしまう。
    322        //      clBuild.disableCookieManagement();
    323
    324                return clBuild.build();         // HttpClient httpClient  = HttpClientBuilder.create().*****.build();
    325        }
    326
    327        /**
    328         * 接続先に使用する引数(ﾊﾟﾗﾒｰﾀ)を追加します。
    329         *
    330         * これは、POSTでも、GETでも使用できます。
    331         * POSTの場合は、NameValuePair として、HttpPost に、Entity としてセットするデータを設定します。
    332         * GET の場合は、既存の接続先URLに、&amp;ｷｰ=値････ で、追記します。
    333         * すでに、ﾊﾟﾗﾒｰﾀが指定済みの場合は、&amp; で、そうでなければ、? で連結します。
    334         * ここで指定するﾊﾟﾗﾒｰﾀは、内部で、urlEncode しますので、そのままの文字列でかまいません。
    335         *
    336         * ﾃﾞﾌｫﾙﾄは、GETですが、Internet Explorer では URL に最大 2,083 文字しか指定できないため、
    337         * それ以上の場合は、POST に自動で切り替えます。
    338         *
    339         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    340         *
    341         * @param       key     パラメータキー(nullの場合は、登録しません)
    342         * @param       val     パラメータ値
    343         */
    344        public void addRequestProperty( final String key, final String val ) {
    345                if( !StringUtil.isNull( key ) ) {
    346                        reqParamList.add( new BasicNameValuePair( key,val ) );                          // POST のときのﾊﾟﾗﾒｰﾀ。(GETでも使えるはず？)
    347
    348                        if( !isPost ) {                                                                                                         // 明らかに、GET でない場合は、この処理を行わない。
    349                                if( reqParamBuf.length() == 0 ) {                                                               // 初めての場合
    350                                        reqParamBuf.append( urlStr )
    351                                                                .append( urlStr.indexOf( '?' ) > 0 ? '&' : '?' )
    352                                                                .append( StringUtil.urlEncode2( key ) )
    353                                                                .append( '=' )
    354                                                                .append( StringUtil.urlEncode2( val ) );                // null のときは、長さゼロ文字列になる。
    355                                }
    356                                else if( reqParamBuf.length() > MAX_GET_LENGTH ) {
    357                                        System.out.println( "GET → POST変更： URLの長さ制限＜" + reqParamBuf.length() );
    358                                        isPost = true;                                                                                          // GETで送れるURLの長さ制限を超えた場合は、POSTにする。
    359                                }
    360                                else {
    361                                        reqParamBuf.append( '&' )
    362                                                                .append( StringUtil.urlEncode2( key ) )
    363                                                                .append( '=' )
    364                                                                .append( StringUtil.urlEncode2( val ) );                // null のときは、長さゼロ文字列になる。
    365                                }
    366                        }
    367                }
    368        }
    369
    370        /**
    371         * setRequestPropertyでセットするデータを設定します。
    372         *
    373         * keys,vals各々、カンマ区切りで分解します。
    374         *
    375         * @og.rev 5.10.16.0 (2019/10/04) 追加
    376         *
    377         * @param       keys    パラメータキー（カンマ区切り）
    378         * @param       vals    パラメータ（カンマ区切り）
    379         */
    380        public void setRequestProperty( final String keys, final String vals ) {
    381                if( keys != null && keys.length() > 0 && vals != null && vals.length() > 0 ){
    382                        final String[]  propKeys = StringUtil.csv2Array( keys );
    383                        final String[]  propVals = StringUtil.csv2Array( vals );
    384
    385                        if( propKeys.length == propVals.length && propKeys.length > 0 ) {
    386                                for( int i=0; i<propKeys.length; i++ ) {
    387                                        addRequestProperty( propKeys[i], propVals[i] );
    388                                }
    389                        }
    390                        else {
    391                                final String errMsg = "パラメータのキーと、値の数が一致しません。"   + CR
    392                                                        + " key=[" + keys + "]"                                                                 + CR
    393                                                        + " val=[" + vals + "]" ;
    394                                throw new IllegalArgumentException( errMsg );
    395                        }
    396                }
    397        }
    398
    399        /**
    400         * 指定のURLに対して、コネクトするのに使用するプロキシ設定を行います。
    401         * このときに、ヘッダー情報を内部変数に設定しておきます。
    402         *
    403         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    404         *
    405         * @param       host    接続するプロキシのホスト名(nullの場合は、登録しません)
    406         * @param       port    接続するプロキシのポート番号
    407         */
    408        public void setProxy( final String host,final int port ) {
    409                if( !StringUtil.isNull( host ) ) {
    410                        proxy = new HttpHost( host , port );
    411                }
    412        }
    413
    414        /**
    415         * Header として、HttpClient にセットするデータを設定します。
    416         *
    417         * 例えばJSON形式でPOSTする場合は通常"Content-Type", "application/json"を指定します。
    418         *
    419         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    420         *
    421         * @param       key     パラメータキー(nullの場合は、登録しません)
    422         * @param       val     パラメータ値(nullの場合は、登録しません)
    423         */
    424        public void addHeaderProperty( final String key, final String val ) {
    425                if( !StringUtil.isNull( key ) && !StringUtil.isNull( val ) ) {
    426                        headers.add( new BasicHeader( key,val ) );
    427                }
    428        }
    429
    430        /**
    431         * URL接続先のバイナリファイルをダウンロード取得します。
    432         *
    433         * 取得したファイルは、dwldFile にバイナリのまま書き込まれます。
    434         * よって、エンコードの指定は不要です。
    435         *
    436         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    437         *
    438         * @param       dwldFile ﾀﾞｳﾝﾛｰﾄﾞするファイル名。
    439         * @throws  IOException 入出力エラーが発生したとき
    440         */
    441        public void setDownloadFile( final String dwldFile ) throws IOException {
    442                this.dwldFile = dwldFile;
    443        }
    444
    445        /**
    446         * URL接続先のバイナリファイルをアップロードします。
    447         *
    448         * 取得したファイルは、upldFile にバイナリのまま書き込まれます。
    449         * よって、エンコードの指定は不要です。
    450         * アップロード は、multipart/form-data で送信するため、isPost = true を
    451         * 内部的に設定しておきます。
    452         *
    453         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    454         * @og.rev 7.2.5.0 (2020/06/01) upldFileのnull判定を入れます。
    455         *
    456         * @param       upldFile ｱｯﾌﾟﾛｰﾄﾞするファイル名。
    457         * @throws  IOException 入出力エラーが発生したとき
    458         */
    459        public void setUploadFile( final String upldFile ) throws IOException {
    460                if( upldFile != null ) {
    461                        this.upldFile = upldFile;
    462                        isPost = true;
    463                }
    464        }
    465
    466        /**
    467         * エンコード情報を設定します。
    468         *
    469         * 初期値は、UTF-8 です。
    470         *
    471         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    472         *
    473         * @param  chset エンコード情報(nullの場合は、初期値:UTF-8 になります)
    474         */
    475        public void setCharset( final String chset ) {
    476                if( !StringUtil.isNull( chset ) ) {
    477                        charset = chset;
    478                }
    479        }
    480
    481        /**
    482         * 接続タイムアウト時間を(秒)で指定します
    483         *
    484         * 実際には、org.apache.http.client.config.RequestConfig に対して、
    485         *       .setConnectTimeout( timeout * 1000 )
    486         *       .setSocketTimeout(  timeout * 1000 )
    487         * のように、 1000倍して設定しています。
    488         * 0 は、無限のタイムアウト、マイナスは、設定しません。(つまりJavaの初期値のまま)
    489         *
    490         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    491         *
    492         * @param       tout    タイムアウト時間(秒) (ゼロは、無制限)
    493         */
    494        public void setTimeout( final int tout ) {
    495                timeout = tout;
    496        }
    497
    498        /**
    499         * trueの場合、POSTを使用して接続します(初期値:false)。
    500         *
    501         * 通常はGETですが、外部から強制的に、POSTで送信したい場合に、
    502         * 設定します。
    503         * ただし、バイナリファイルをアップロードか、URLの長さ制限が、
    504         * {@value #MAX_GET_LENGTH} を超えた場合は、内部で自動的に、post にします。
    505         *
    506         * @og.rev 6.9.0.1 (2018/02/05) 新規作成
    507         *
    508         * @param       usePost true:POST使用/false:通常(GET)
    509         */
    510        public void usePost( final boolean usePost ) {
    511                isPost = usePost;
    512        }
    513
    514        /**
    515         * trueの場合、POST時に強制的にﾘﾀﾞｲﾚｸﾄを行います(初期値:false)。
    516         *
    517         * @og.rev 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にﾘﾀﾞｲﾚｸﾄ)
    518         *
    519         * @param       useRedirect     true:POST時に強制的にﾘﾀﾞｲﾚｸﾄ/false:通常
    520         */
    521        public void setPostRedirect( final boolean useRedirect ) {
    522                postRedirect = useRedirect;
    523        }
    524
    525        /**
    526         * trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)。
    527         *
    528         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    529         *
    530         * @param       isDebug true:デバッグ用のメッセージを出力/false:通常
    531         */
    532        public void setDebug( final boolean isDebug ) {
    533                this.isDebug = isDebug;
    534        }
    535
    536        /**
    537         * 実行結果のステータスコード 情報を取得します。
    538         *
    539         * 結果は、#readData() メソッドをコールしないと取れません。
    540         * 未実行の場合は、-1 がセットされています。
    541         *
    542         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    543         *
    544         * @return      結果コード 情報
    545         * @see         #readData()
    546         */
    547        public int getCode() { return rpsCode; }
    548
    549        /**
    550         * メッセージ 情報を取得します。
    551         *
    552         * 結果は、#readData() メソッドをコールしないと取れません。
    553         * 未実行の場合は、null がセットされています。
    554         *
    555         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    556         *
    557         * @return      メッセージ 情報
    558         */
    559        public String getMessage() { return rpsMessage; }
    560
    561        /**
    562         * HttpURLConnection のレスポンスコードに対応するメッセージ文字列を返します。
    563         *
    564         * HttpURLConnection の getResponseCode() メソッドにより取得された、HTTPレスポンスコード
    565         * に対応する文字列を返します。この文字列は、HttpURLConnection で定義された
    566         * static 定数のコメントを、定義しています。
    567         *
    568         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    569         *
    570         * @param       code    HTTPレスポンスコード
    571         *
    572         * @return      レスポンスコードに対応する文字列
    573         * @og.rtnNotNull
    574         * @see HttpURLConnection#HTTP_ACCEPTED
    575         */
    576        public static String code2Message( final int code ) {
    577                final String msg ;
    578                switch( code ) {
    579                        case 100                                                                                : msg = "100: 要求は続行可能です。"                                               ;       break;
    580                        case 101                                                                                : msg = "101: プロトコルを切り替えます。"                            ;       break;
    581                        case HttpURLConnection.HTTP_OK                                  : msg = "200: OK です。"                                                           ;       break;
    582                        case HttpURLConnection.HTTP_CREATED                     : msg = "201: 作成されました。"                                                 ;       break;
    583                        case HttpURLConnection.HTTP_ACCEPTED                    : msg = "202: 受け入れられました。"                                               ;       break;
    584                        case HttpURLConnection.HTTP_NOT_AUTHORITATIVE   : msg = "203: 信頼できない情報です。"                                      ;       break;
    585                        case HttpURLConnection.HTTP_NO_CONTENT                  : msg = "204: コンテンツがありません。"                                     ;       break;
    586                        case HttpURLConnection.HTTP_RESET                               : msg = "205: コンテンツをリセットします。"                           ;       break;
    587                        case HttpURLConnection.HTTP_PARTIAL                     : msg = "206: 部分的なコンテンツです。"                                     ;       break;
    588                        case HttpURLConnection.HTTP_MULT_CHOICE                 : msg = "300: 複数の選択肢があります。"                                     ;       break;
    589                        case HttpURLConnection.HTTP_MOVED_PERM                  : msg = "301: 永続的に移動されました。"                                     ;       break;
    590                        case HttpURLConnection.HTTP_MOVED_TEMP                  : msg = "302: 一時的なリダイレクト。"                                      ;       break;
    591                        case HttpURLConnection.HTTP_SEE_OTHER                   : msg = "303: ほかを参照してください。"                                     ;       break;
    592                        case HttpURLConnection.HTTP_NOT_MODIFIED                : msg = "304: 変更されていません。"                                               ;       break;
    593                        case HttpURLConnection.HTTP_USE_PROXY                   : msg = "305: プロキシを使用します。"                                      ;       break;
    594                        case 306                                                                                : msg = "306: 仕様の拡張案です。"                                                ;       break;
    595                        case 307                                                                                : msg = "307: 一時的なリダイレクトです。"                            ;       break;
    596                        case HttpURLConnection.HTTP_BAD_REQUEST                 : msg = "400: 不当な要求です。"                                                 ;       break;
    597                        case HttpURLConnection.HTTP_UNAUTHORIZED                : msg = "401: 認証されませんでした。"                                      ;       break;
    598                        case HttpURLConnection.HTTP_PAYMENT_REQUIRED    : msg = "402: 支払いが必要です。"                                                ;       break;
    599                        case HttpURLConnection.HTTP_FORBIDDEN                   : msg = "403: 禁止されています。"                                                ;       break;
    600                        case HttpURLConnection.HTTP_NOT_FOUND                   : msg = "404: 見つかりませんでした。"                                      ;       break;
    601                        case HttpURLConnection.HTTP_BAD_METHOD                  : msg = "405: メソッドは許可されません。"                            ;       break;
    602                        case HttpURLConnection.HTTP_NOT_ACCEPTABLE              : msg = "406: 受け入れられません。"                                               ;       break;
    603                        case HttpURLConnection.HTTP_PROXY_AUTH                  : msg = "407: プロキシの認証が必要です。"                            ;       break;
    604                        case HttpURLConnection.HTTP_CLIENT_TIMEOUT              : msg = "408: 要求がタイムアウトしました。"                           ;       break;
    605                        case HttpURLConnection.HTTP_CONFLICT                    : msg = "409: 重複しています。"                                                 ;       break;
    606                        case HttpURLConnection.HTTP_GONE                                : msg = "410: 存在しません。"                                                  ;       break;
    607                        case HttpURLConnection.HTTP_LENGTH_REQUIRED     : msg = "411: 長さが必要です。"                                                 ;       break;
    608                        case HttpURLConnection.HTTP_PRECON_FAILED               : msg = "412: 前提条件が満たされていません。"                  ;       break;
    609                        case HttpURLConnection.HTTP_ENTITY_TOO_LARGE    : msg = "413: 要求のエンティティが大きすぎます。"                ;       break;
    610                        case HttpURLConnection.HTTP_REQ_TOO_LONG                : msg = "414: 要求のURIが大きすぎます。"                           ;       break;
    611                        case HttpURLConnection.HTTP_UNSUPPORTED_TYPE    : msg = "415: サポートされないメディアタイプです。"               ;       break;
    612                        case 416                                                                                : msg = "416: 要求された範囲は不十分です。"                           ;       break;
    613                        case 417                                                                                : msg = "417: 要求どおりの処理が不可能です。"                  ;       break;
    614                        case HttpURLConnection.HTTP_INTERNAL_ERROR              : msg = "500: 内部サーバエラーです。"                                      ;       break;
    615                        case HttpURLConnection.HTTP_NOT_IMPLEMENTED     : msg = "501: 実装されていません。"                                               ;       break;
    616                        case HttpURLConnection.HTTP_BAD_GATEWAY                 : msg = "502: 誤ったゲートウェイです。"                                     ;       break;
    617                        case HttpURLConnection.HTTP_UNAVAILABLE                 : msg = "503: サービスが利用できません。"                            ;       break;
    618                        case HttpURLConnection.HTTP_GATEWAY_TIMEOUT     : msg = "504: ゲートウェイがタイムアウトしました。"               ;       break;
    619                        case HttpURLConnection.HTTP_VERSION                     : msg = "505: サポートされていないHTTPバージョンです。"   ;       break;
    620                        default                                                                                 : msg = code + ": 未定義"                                                          ;       break;
    621                }
    622                return msg ;
    623        }
    624
    625        /**
    626         * サンプル実行用のメインメソッド
    627         *
    628         * <pre>
    629         * Usage: java org.opengion.fukurou.util.HttpConnect [-post=キー:ファイル名] … url [user:passwd]
    630         *   args[A] : url                     ＵＲＬを指定します。GETの場合、パラメータは ?KEY=VALです
    631         *   args[*] : [-param=key:value]      POST/GET時のﾊﾟﾗﾒｰﾀのｷｰと値を:で区切って指定します。(複数回指定可)
    632         *   args[*] : [-header=key:value]     ﾍｯﾀﾞｰに設定するﾊﾟﾗﾒｰﾀのｷｰと値を:で区切って指定します。(複数回指定可)
    633         *   args[*] : [-auth=user:pass]       BASIC認証のエリアへのアクセス時のユーザーとパスワードを指定します
    634         *   args[*] : [-proxy=host:port]      proxy を使用する場合のホストとポートを指定します。
    635         *   args[*] : [-timeout=3]            接続タイムアウト時間を(秒)で指定します(初期値:無指定)
    636         *   args[*] : [-encode=UTF-8]         エンコードを指定します。(初期値は UTF-8)
    637         *   args[*] : [-out=ファイル名]       結果をファイルに出力します。初期値は標準出力です
    638         *   args[*] : [-download=ファイル名]  ﾌｧｲﾙ名を指定して、ﾀﾞｳﾝﾛｰﾄﾞします
    639         *   args[*] : [-upload=ファイル名]    ﾌｧｲﾙ名を指定して、multipart/form-dataでﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞします
    640         *   args[*] : [-postRedirect=true]    POST時に強制的にﾘﾀﾞｲﾚｸﾄを行います(GET時は自動でﾘﾀﾞｲﾚｸﾄします)(初期値:false) 7.2.5.0 (2020/06/01)
    641         *   args[*] : [-errEx=true/false]     trueの場合、ﾚｽﾎﾟﾝｽｺｰﾄﾞが、4XX,5XX の時に RuntimeException を投げます(初期値:false)
    642         *   args[*] : [#････]                 ｺﾒﾝﾄ引数。(BATﾌｧｲﾙ上に残しておきたいが、使用したくない場合など)
    643         *   args[*] : [-debug=true/false]     trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)
    644         * </pre>
    645         *
    646         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
    647         * @og.rev 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にﾘﾀﾞｲﾚｸﾄ)引数を追加
    648         *
    649         * @param       args    コマンド引数配列
    650         * @throws IOException 入出力エラーが発生したとき
    651         */
    652        public static void main( final String[] args ) throws IOException {
    653                if( args.length < 2 ) {
    654                        LogWriter.log( "Usage: java org.opengion.fukurou.util.HttpConnect [-data/-binary] … url"                                                                                                        );
    655                        LogWriter.log( "   args[A] : url                     ＵＲＬを指定します。GETの場合、パラメータは ?KEY=VALです"                                                                );
    656                        LogWriter.log( "   args[*] : [-param=key:value]      POST/GET時のﾊﾟﾗﾒｰﾀのｷｰと値を:で区切って指定します。(複数回指定可)"                                                );
    657                        LogWriter.log( "   args[*] : [-header=key:value]     ﾍｯﾀﾞｰに設定するﾊﾟﾗﾒｰﾀのｷｰと値を:で区切って指定します。(複数回指定可)"                                        );
    658                        LogWriter.log( "   args[*] : [-auth=user:pass]       BASIC認証のエリアへのアクセス時のユーザーとパスワードを指定します"                                               );
    659                        LogWriter.log( "   args[*] : [-proxy=host:port]      proxy を使用する場合のホストとポートを指定します。"                                                                      );
    660                        LogWriter.log( "   args[*] : [-timeout=3]            接続タイムアウト時間を(秒)で指定します(初期値:無指定)"                                                                     );
    661                        LogWriter.log( "   args[*] : [-encode=UTF-8]         エンコードを指定します。(初期値は UTF-8)"                                                                                          );
    662                        LogWriter.log( "   args[*] : [-out=ファイル名]       結果をファイルに出力します。初期値は標準出力です"                                                                               );
    663                        LogWriter.log( "   args[*] : [-download=ファイル名]  ﾌｧｲﾙ名を指定して、ﾀﾞｳﾝﾛｰﾄﾞします"                                                                                                         );
    664                        LogWriter.log( "   args[*] : [-upload=ファイル名]    ﾌｧｲﾙ名を指定して、multipart/form-dataでﾌｧｲﾙｱｯﾌﾟﾛｰﾄﾞします"                                                         );
    665                        LogWriter.log( "   args[*] : [-postRedirect=true]    POST時に強制的にﾘﾀﾞｲﾚｸﾄを行います(GET時は自動でﾘﾀﾞｲﾚｸﾄします)(初期値:false)"                     );
    666                        LogWriter.log( "   args[*] : [-errEx=true/false]     trueの場合、ﾚｽﾎﾟﾝｽｺｰﾄﾞが、4XX,5XX の時に RuntimeException を投げます(初期値:false)" );
    667                        LogWriter.log( "   args[*] : [#････]                 ｺﾒﾝﾄ引数。(BATﾌｧｲﾙ上に残しておきたいが、使用したくない場合など)"                                            );
    668                        LogWriter.log( "   args[*] : [-debug=true/false]     trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)"                                   );
    669                        return;
    670                }
    671
    672                String  urlStr                  = null ;
    673                final List<String> paramKey  = new ArrayList<>();       // ﾊﾟﾗﾒｰﾀｰｷｰ
    674                final List<String> paramVal  = new ArrayList<>();       // ﾊﾟﾗﾒｰﾀｰ値
    675                final List<String> headerKey = new ArrayList<>();       // ﾊﾟﾗﾒｰﾀｰｷｰ
    676                final List<String> headerVal = new ArrayList<>();       // ﾊﾟﾗﾒｰﾀｰ値
    677
    678                String  userPass                = null ;
    679                String  proxy                   = null ;
    680                int             timeout                 = -1 ;
    681                String  encode                  = DEFAULT_CHARSET ;
    682                String  outFile                 = null ;
    683                String  dwldFile                = null ;
    684                String  upldFile                = null ;
    685                boolean isEx                    = false ;
    686                boolean isDebug                 = false ;
    687                boolean postRedirect    = false ;                       // 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にﾘﾀﾞｲﾚｸﾄ)
    688                boolean nonWriter               = false ;
    689
    690//              int             code                    = -1;
    691
    692                for( final String arg : args ) {
    693                        if( arg.startsWith( "-param=" ) ) {
    694                                final String[] prm = StringUtil.csv2Array( arg.substring( "-param=".length() ) , '=' , 2 );
    695                                paramKey.add( prm[0] );
    696                                paramVal.add( prm[1] );
    697                        }
    698                        else if( arg.startsWith( "-header=" ) ) {
    699                                final String[] prm = StringUtil.csv2Array( arg.substring( "-header=".length() ) , '=' , 2 );
    700                                headerKey.add( prm[0] );
    701                                headerVal.add( prm[1] );
    702                        }
    703                        else if( arg.startsWith( "-auth=" ) ) {
    704                                userPass = arg.substring( "-auth=".length() );
    705                                if( StringUtil.isNull( userPass ) ) {
    706                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
    707                                }
    708                        }
    709                        else if( arg.startsWith( "-proxy=" ) ) {
    710                                proxy = arg.substring( "-proxy=".length() );
    711                                if( StringUtil.isNull( proxy ) ) {
    712                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
    713                                }
    714                        }
    715                        else if( arg.startsWith( "-timeout=" ) ) {
    716                                timeout = Integer.parseInt( arg.substring( "-timeout=".length() ) );
    717                        }
    718                        else if( arg.startsWith( "-encode=" ) ) {
    719                                encode = arg.substring( "-encode=".length() );
    720                                if( StringUtil.isNull( encode ) ) {
    721                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
    722                                }
    723                        }
    724                        else if( arg.startsWith( "-out=" ) ) {
    725                                outFile = arg.substring( "-out=".length() );
    726                                if( StringUtil.isNull( outFile ) ) {
    727                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
    728                                }
    729                                else {
    730                                        if( "null".equalsIgnoreCase( outFile ) || "none".equalsIgnoreCase( outFile ) ) {
    731                                                outFile   = null;
    732                                                nonWriter = true;
    733                                        }
    734                                }
    735                        }
    736                        else if( arg.startsWith( "-download=" ) ) {
    737                                dwldFile = arg.substring( "-download=".length() );
    738                                if( StringUtil.isNull( dwldFile ) ) {
    739                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
    740                                }
    741                        }
    742                        else if( arg.startsWith( "-upload=" ) ) {
    743                                upldFile = arg.substring( "-upload=".length() );
    744                                if( StringUtil.isNull( upldFile ) ) {
    745                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
    746                                }
    747                        }
    748                        else if( arg.startsWith( "-errEx=" ) ) {
    749                                isEx = "true".equalsIgnoreCase( arg.substring( "-errEx=".length() ) );
    750                        }
    751                        // 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にﾘﾀﾞｲﾚｸﾄ)
    752                        else if( arg.startsWith( "-postRedirect=" ) ) {
    753                                postRedirect = "true".equalsIgnoreCase( arg.substring( "-postRedirect=".length() ) );
    754                        }
    755                        else if( arg.startsWith( "-debug=" ) ) {
    756                                isDebug = "true".equalsIgnoreCase( arg.substring( "-debug=".length() ) );
    757                        }
    758                        else if( StringUtil.startsChar( arg , '-' ) ) {                 // 引数が未定義(処理は継続させます。)
    759                                System.err.println( "Error Argment:" + arg );
    760                        }
    761                        else if( StringUtil.startsChar( arg , '#' ) ) {                 // 引数がコメント
    762                                continue;
    763                        }
    764                        else {
    765                                urlStr = arg;
    766                        }
    767                }
    768
    769                try {                                                                   // try catch を入れます。
    770                        final HttpConnect conn = new HttpConnect( urlStr,userPass );
    771                        conn.setDebug( isDebug );                       // 最初に入れておけば、それ以降、有効になります。
    772
    773                        for( int i=0; i<paramKey.size(); i++ ) {
    774                                conn.addRequestProperty( paramKey.get(i) , paramVal.get(i) );
    775                        }
    776
    777                        for( int i=0; i<headerKey.size(); i++ ) {
    778                                conn.addHeaderProperty( headerKey.get(i) , headerVal.get(i) );
    779                        }
    780
    781                        // 6.8.1.3 (2017/08/04) proxy の設定
    782                        if( !StringUtil.isNull( proxy ) ) {
    783                                final String[] prm = StringUtil.csv2Array( proxy , ':' , 2 );
    784                                final String host = prm[0];
    785                                final int    port = Integer.parseInt( prm[1] );
    786                                conn.setProxy( host , port );
    787                        }
    788
    789                        conn.setCharset(                encode );               // encode 指定
    790                        conn.setTimeout(                timeout );              // timeout属性追加
    791                        conn.setUploadFile(             upldFile );
    792                        conn.setDownloadFile(   dwldFile );
    793                        conn.setPostRedirect(   postRedirect ); // 7.2.5.0 (2020/06/01)
    794
    795                        final String outData = conn.readData();
    796
    797                        try( PrintWriter writer = StringUtil.isNull( outFile )
    798                                                                                                ? FileUtil.getLogWriter( "System.out" )
    799                                                                                                : FileUtil.getPrintWriter( new File( outFile ),encode ) ) {
    800                                if( !nonWriter ) {
    801                                        writer.println( outData );
    802                                }
    803                                final int code = conn.getCode();
    804
    805                                // isEx=trueの場合、ﾚｽﾎﾟﾝｽｺｰﾄﾞが、4XX,5XX の時に RuntimeException を投げます
    806                                if( code >= 400 ) {
    807                                        final String errMsg = conn.getMessage();
    808                                        writer.println( errMsg );
    809                                        if( isEx ) {
    810                                                throw new OgRuntimeException( errMsg );
    811                                        }
    812                                        else {
    813                                                System.exit( code );
    814                                        }
    815                                }
    816                        }
    817                }
    818                catch( final Throwable th ) {
    819                        throw new OgRuntimeException( th );
    820                }
    821        }
    822}