001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com).
003 * <p>
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 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.jboot.components.http.okhttp;
017
018import io.jboot.components.http.JbootHttp;
019import io.jboot.components.http.JbootHttpRequest;
020import io.jboot.components.http.JbootHttpResponse;
021import okhttp3.*;
022
023import javax.net.ssl.*;
024import java.io.File;
025import java.security.KeyStore;
026import java.security.SecureRandom;
027import java.security.cert.X509Certificate;
028import java.util.Map;
029
030/**
031 * @author Michael Yang 杨福海 (fuhai999@gmail.com)
032 * @version V1.0
033 */
034public class OKHttpImpl implements JbootHttp {
035
036    public OKHttpImpl() {
037
038    }
039
040    @Override
041    public JbootHttpResponse handle(JbootHttpRequest request) {
042        JbootHttpResponse response = new JbootHttpResponse(request);
043        doProcess(request, response);
044        return response;
045    }
046
047
048    private void doProcess(JbootHttpRequest request, JbootHttpResponse response) {
049        try {
050            // post 请求 或者 put 请求
051            if (request.isPostOrPutRequest()) {
052                if (request.getBodyContent() != null) {
053                    request.appendParasToUrl();
054                }
055                doProcessPostRequest(request, response);
056            }
057
058            // 其他非 post 和 put 请求
059            else {
060                request.appendParasToUrl();
061                doProcessGetRequest(request, response);
062            }
063
064        } catch (Throwable ex) {
065            response.setError(ex);
066        } finally {
067            response.close();
068        }
069    }
070
071    private void doProcessGetRequest(JbootHttpRequest request, JbootHttpResponse response) throws Exception {
072        Request okHttpRequest = new Request.Builder()
073                .url(request.getRequestUrl())
074                .build();
075
076
077        doProcessRequest(request, response, okHttpRequest);
078    }
079
080    private void doProcessPostRequest(final JbootHttpRequest request, JbootHttpResponse response) throws Exception {
081        RequestBody requestBody = null;
082        if (request.isMultipartFormData()) {
083            MultipartBody.Builder builder = new MultipartBody.Builder();
084            for (Map.Entry<String, Object> entry : request.getParams().entrySet()) {
085                if (entry.getValue() instanceof File) {
086                    File file = (File) entry.getValue();
087                    builder.addFormDataPart(entry.getKey(), file.getName(), RequestBody.create(MediaType.parse("application/octet-stream"), file));
088                } else {
089                    builder.addFormDataPart(entry.getKey(), entry.getValue() == null ? "" : entry.getValue().toString());
090                }
091            }
092            requestBody = builder.build();
093        } else {
094//            FormBody.Builder builder = new FormBody.Builder();
095//            for (Map.Entry<String, Object> entry : request.getParams().entrySet()) {
096//                builder.add(entry.getKey(), entry.getValue() == null ? "" : entry.getValue().toString());
097//            }
098//            requestBody = builder.build();
099
100            MediaType mediaType = MediaType.parse(request.getContentType());
101            requestBody = RequestBody.create(mediaType, request.getUploadBodyString());
102        }
103
104
105        Request okHttpRequest = new Request.Builder().url(request.getRequestUrl())
106                .post(requestBody)
107                .build();
108
109
110        doProcessRequest(request, response, okHttpRequest);
111    }
112
113    private void doProcessRequest(JbootHttpRequest request, JbootHttpResponse response, Request okHttpRequest) throws Exception {
114        OkHttpClient client = getClient(request);
115        Call call = client.newCall(okHttpRequest);
116        Response okHttpResponse = call.execute();
117        response.setResponseCode(okHttpResponse.code());
118        response.setContentType(okHttpResponse.body().contentType().type());
119
120        if (request.isReadBody()) {
121            response.copyStream(okHttpResponse.body().byteStream());
122        }
123    }
124
125
126    private OkHttpClient getClient(JbootHttpRequest request) throws Exception {
127        if (request.getRequestUrl().toLowerCase().startsWith("https")) {
128            return getHttpsClient(request);
129        }
130
131
132        OkHttpClient.Builder builder = new OkHttpClient.Builder();
133        if (request.getProxy() != null) {
134            builder.proxy(request.getProxy());
135        }
136
137        return builder.build();
138    }
139
140    public OkHttpClient getHttpsClient(JbootHttpRequest request) throws Exception {
141        OkHttpClient.Builder builder = new OkHttpClient.Builder();
142        //自定义 sslContext
143        if (request.getSslContext() != null) {
144            SSLSocketFactory ssf = request.getSslContext().getSocketFactory();
145            builder.sslSocketFactory(ssf, trustAnyTrustManager);
146        }
147        //配置证书的路径和密码
148        else if (request.getCertPath() != null && request.getCertPass() != null) {
149            KeyStore clientStore = KeyStore.getInstance("PKCS12");
150            clientStore.load(request.getCertInputStream(), request.getCertPass().toCharArray());
151
152            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
153            keyManagerFactory.init(clientStore, request.getCertPass().toCharArray());
154            KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
155
156
157            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
158            trustManagerFactory.init(clientStore);
159
160            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
161
162
163            SSLContext sslContext = SSLContext.getInstance("TLS");
164            sslContext.init(keyManagers, trustManagers, new SecureRandom());
165
166            X509TrustManager x509TrustManager = trustAnyTrustManager;
167            if (trustManagers != null && trustManagers.length > 0 && trustManagers[0] instanceof X509TrustManager) {
168                x509TrustManager = (X509TrustManager) trustManagers[0];
169            }
170
171            builder.sslSocketFactory(sslContext.getSocketFactory(), x509TrustManager);
172
173        } else {
174            builder.hostnameVerifier(hnv);
175            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
176            if (sslContext != null) {
177                TrustManager[] trustManagers = {trustAnyTrustManager};
178                sslContext.init(null, trustManagers, new SecureRandom());
179                builder.sslSocketFactory(sslContext.getSocketFactory(), trustAnyTrustManager);
180            }
181        }
182
183        return builder.build();
184    }
185
186
187    private static X509TrustManager trustAnyTrustManager = new X509TrustManager() {
188        @Override
189        public void checkClientTrusted(X509Certificate[] chain, String authType) {
190        }
191
192        @Override
193        public void checkServerTrusted(X509Certificate[] chain, String authType) {
194        }
195
196        @Override
197        public X509Certificate[] getAcceptedIssuers() {
198            return new X509Certificate[0];
199        }
200    };
201
202    private static HostnameVerifier hnv = (hostname, session) -> true;
203}