All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jianggujin.http.core.JRequestExecuter Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2018 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.http.core;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map.Entry;

import javax.net.ssl.HttpsURLConnection;

import com.jianggujin.http.request.JRequestBodyResolverFactory;
import com.jianggujin.http.util.JDataUtils;
import com.jianggujin.http.util.JKeyVal;
import com.jianggujin.http.util.JOutputStream;

/**
 * 请求执行器
 * 
 * @author jianggujin
 *
 */
public final class JRequestExecuter {
    private static final String CONTENT_TYPE = "Content-Type";
    private static final String MULTIPART_FORM_DATA = "multipart/form-data";
    private static final String FORM_URL_ENCODED = "application/x-www-form-urlencoded";

    private static JRequestExecuterListener requestExecuterListener = null;

    protected static void execute(JRequest req) throws JHttpException {
        if (req == null) {
            throw new IllegalArgumentException("Request must not be null");
        }
        String protocol = req.url().getProtocol();
        if (!"http".equals(protocol) && !"https".equals(protocol)) {
            throw new JHttpException("Only http & https protocols supported");
        }
        final boolean methodHasBody = req.forceHasBody() || req.method().hasBody();
        final boolean hasRequestBody = req.requestBody() != null;
        if (!methodHasBody) {
            if (hasRequestBody) {
                throw new IllegalArgumentException("Cannot set a request body for HTTP method " + req.method());
            }
        }

        String mimeBoundary = null;
        if (hasData(req) && (!methodHasBody || hasRequestBody)) {
            try {
                serialiseRequestUrl(req);
            } catch (IOException e) {
                throw new JHttpException(e);
            }
        } else if (methodHasBody) {
            mimeBoundary = setOutputContentType(req);
        }

        HttpURLConnection conn;
        try {
            conn = createConnection(req);
        } catch (IOException e) {
            throw new JHttpException(e);
        }
        try {
            if (requestExecuterListener != null) {
                requestExecuterListener.beforeConnect(conn, req);
            }
            conn.connect();
            if (conn.getDoOutput()) {
                writePost(req, conn.getOutputStream(), mimeBoundary);
            }
            // 执行一次,内部会调用getInputStream,目的是为了解析响应,如果设置了CookieManager,保证获得Cookie
            // Map> headers = conn.getHeaderFields();
            InputStream stream = conn.getErrorStream() != null ? conn.getErrorStream() : conn.getInputStream();
            JResponse response = req.response();
            if (response != null) {
                try {
                    response.onComplete(stream, conn, req);
                } finally {
                    JDataUtils.close(stream);
                    if (requestExecuterListener != null) {
                        requestExecuterListener.onComplete(conn, req, response);
                    }
                }
            } else if (requestExecuterListener != null) {
                requestExecuterListener.onComplete(conn, req, response);
            }
        } catch (Exception e) {
            throw new JHttpException(e);
        } finally {
            conn.disconnect();
        }
    }

    /***
     * 获得请求cookie
     * 
     * @param req
     * @return
     */
    private static String getRequestCookieString(JRequest req) {
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (Entry cookie : req.cookies().entrySet()) {
            if (!first)
                sb.append("; ");
            else
                first = false;
            sb.append(cookie.getKey()).append('=').append(cookie.getValue() == null ? "" : cookie.getValue());
        }
        return sb.toString();
    }

    /**
     * 序列化请求地址
     * 
     * @param req
     * @throws IOException
     */
    private static void serialiseRequestUrl(JRequest req) throws IOException {
        URL in = req.url();
        StringBuilder url = new StringBuilder();
        boolean first = true;
        url.append(in.getProtocol()).append("://").append(in.getAuthority()) // 包含主机、端口
                .append(in.getPath()).append("?");
        if (in.getQuery() != null) {
            url.append(in.getQuery());
            first = false;
        }
        if (hasData(req)) {
            for (JKeyVal keyVal : req.data()) {
                if (keyVal.hasInputStream()) {
                    throw new IllegalArgumentException("InputStream data not supported in URL query string.");
                }
                if (!first) {
                    url.append('&');
                } else {
                    first = false;
                }
                url.append(keyVal.key()).append('=')
                        .append(keyVal.value() == null ? "" : URLEncoder.encode(keyVal.value(), req.charset()));
            }
            req.data().clear();
        }
        req.url(new URL(url.toString()));
    }

    /**
     * 设置Content-Type
     * 
     * @param req
     * @return
     */
    private static String setOutputContentType(final JRequest req) {
        String bound = null;
        if (needsMultipart(req)) {
            bound = JDataUtils.mimeBoundary();
            req.header(CONTENT_TYPE, MULTIPART_FORM_DATA + "; boundary=" + bound);
        } else if (!req.hasHeader(CONTENT_TYPE)) {// 如果没有设置,则设置默认值,否则会被覆盖
            req.header(CONTENT_TYPE, FORM_URL_ENCODED + "; charset=" + req.charset());
        }
        return bound;
    }

    /**
     * 判断是否需要多部分
     * 
     * @param req
     * @return
     */
    private static boolean needsMultipart(JRequest req) {
        boolean needsMulti = false;
        if (hasData(req)) {
            for (JKeyVal keyVal : req.data()) {
                if (keyVal.hasInputStream()) {
                    needsMulti = true;
                    break;
                }
            }
        }
        return needsMulti;
    }

    /**
     * 创建连接
     * 
     * @param req
     * @return
     * @throws IOException
     */
    private static HttpURLConnection createConnection(JRequest req) throws IOException {
        final HttpURLConnection conn = (HttpURLConnection) (req.proxy() == null ? req.url().openConnection()
                : req.url().openConnection(req.proxy()));

        conn.setRequestMethod(req.method().name());
        conn.setInstanceFollowRedirects(true);
        conn.setConnectTimeout(req.timeout());
        conn.setReadTimeout(req.timeout());

        if (conn instanceof HttpsURLConnection) {
            JSSLContextFactory sslSocketFactory = req.sslContextFactory();
            if (sslSocketFactory != null) {
                ((HttpsURLConnection) conn).setSSLSocketFactory(sslSocketFactory.getSSLContext().getSocketFactory());
                ((HttpsURLConnection) conn).setHostnameVerifier(sslSocketFactory.getHostnameVerifier());
            }
        }

        if (req.forceHasBody() || req.method().hasBody()) {
            conn.setDoOutput(true);
        }
        if (req.cookies() != null && req.cookies().size() > 0) {
            conn.addRequestProperty("Cookie", getRequestCookieString(req));
        }
        if (req.headers() != null && req.headers().size() > 0) {
            for (Entry header : req.headers().entrySet()) {
                conn.setRequestProperty(header.getKey(), header.getValue() == null ? "" : header.getValue());
            }
        }
        return conn;
    }

    /**
     * 写POST数据
     * 
     * @param req
     * @param outputStream
     * @param bound
     * @throws IOException
     */
    private static void writePost(final JRequest req, final OutputStream outputStream, final String bound)
            throws IOException {
        // final List data = req.data();
        final JOutputStream stream = new JOutputStream(outputStream, req.charset());
        try {
            if (bound != null) {
                // boundary will be set if we're in multipart mode
                if (hasData(req)) {
                    for (JKeyVal keyVal : req.data()) {
                        stream.write("--");
                        stream.write(bound);
                        stream.write("\r\n");
                        stream.write("Content-Disposition: form-data; name=\"");
                        stream.write(JDataUtils.encodeMimeName(keyVal.key()));
                        // %22
                        stream.write("\"");
                        if (keyVal.hasInputStream()) {
                            stream.write("; filename=\"");
                            stream.write(JDataUtils.encodeMimeName(keyVal.value()));
                            stream.write("\"\r\nContent-Type: application/octet-stream\r\n\r\n");
                            stream.flush(); // flush
                            JDataUtils.crossStreams(keyVal.inputStream(), outputStream);
                            JDataUtils.close(keyVal.inputStream());
                            outputStream.flush();
                        } else {
                            stream.write("\r\n\r\n");
                            stream.write(keyVal.value() == null ? "" : keyVal.value());
                        }
                        stream.write("\r\n");
                    }
                }
                stream.write("--");
                stream.write(bound);
                stream.write("--");
            } else if (req.requestBody() != null) {
                JRequestBodyResolver resolver = req.requestBodyResolver();
                if (resolver == null) {
                    Class clazz = req.requestBody().getClass();
                    resolver = JRequestBodyResolverFactory.getFirstSupportResolver(clazz);
                }
                if (resolver == null) {
                    throw new IllegalArgumentException("not found RequestBodyResolver");
                }
                resolver.write(req, req.requestBody(), stream);
            } else {
                if (hasData(req)) {
                    // regular form data (application/x-www-form-urlencoded)
                    boolean first = true;
                    for (JKeyVal keyVal : req.data()) {
                        if (!first) {
                            stream.write("&");
                        } else {
                            first = false;
                        }
                        stream.write(keyVal.key());
                        stream.write("=");
                        stream.write(keyVal.value() == null ? "" : URLEncoder.encode(keyVal.value(), req.charset()));
                    }
                }
            }
            stream.flush();
        } catch (IOException e) {
            throw e;
        } finally {
            stream.close();
        }
    }

    /**
     * 设置请求监听
     * 
     * @param listener
     */
    public static void setRequestExecuterListener(JRequestExecuterListener listener) {
        JRequestExecuter.requestExecuterListener = listener;
    }

    /**
     * 开启调试
     * 
     * @param debug
     */
    public static void enableDebug(boolean debug) {
        System.setProperty("javax.net.debug", debug ? "all" : null);
    }

    /**
     * 判断是否有数据
     * 
     * @param req
     * @return
     */
    private static boolean hasData(JRequest req) {
        return req.data() != null && req.data().size() > 0;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy