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

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

package com.jianggujin.http.core;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.List;
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 IOException {
      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 MalformedURLException("Only http & https protocols supported");
      }
      final boolean methodHasBody = 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());
         }
      }

      // set up the request for execution
      String mimeBoundary = null;
      if (req.data().size() > 0 && (!methodHasBody || hasRequestBody)) {
         serialiseRequestUrl(req);
      } else if (methodHasBody) {
         mimeBoundary = setOutputContentType(req);
      }

      HttpURLConnection conn = createConnection(req);
      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);
         }
      } 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());
         // todo: spec says only ascii, no escaping / encoding defined. validate
         // on set? or escape somehow here?
      }
      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;
      // reconstitute the query, ready for appends
      url.append(in.getProtocol()).append("://").append(in.getAuthority()) // includes
                                                                           // host,
                                                                           // port
            .append(in.getPath()).append("?");
      if (in.getQuery() != null) {
         url.append(in.getQuery());
         first = false;
      }
      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(URLEncoder.encode(keyVal.value(), req.charset()));
      }
      req.url(new URL(url.toString()));
      req.data().clear(); // moved into url as get params
   }

   /**
    * 设置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) {
      // multipart mode, for files. add the header if we see something with an
      // inputstream, and return a non-null boundary
      boolean needsMulti = false;
      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.method().hasBody()) {
         conn.setDoOutput(true);
      }
      if (req.cookies().size() > 0) {
         conn.addRequestProperty("Cookie", getRequestCookieString(req));
      }
      for (Entry header : req.headers().entrySet()) {
         conn.setRequestProperty(header.getKey(), 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());
      // final OutputStreamWriter streamWriter = new
      // OutputStreamWriter(outputStream, req.postDataCharset());
      try {
         if (bound != null) {
            // boundary will be set if we're in multipart mode
            for (JKeyVal keyVal : data) {
               stream.write("--");
               stream.write(bound);
               stream.write("\r\n");
               stream.write("Content-Disposition: form-data; name=\"");
               stream.write(JDataUtils.encodeMimeName(keyVal.key())); // encodes
                                                                      // "
                                                                      // to
               // %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());
               }
               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.getResolver(clazz);
            }
            if (resolver == null) {
               throw new IllegalArgumentException("not found defaultRequestBodyResolver");
            }
            resolver.write(req, req.requestBody(), stream);
         } else {
            // regular form data (application/x-www-form-urlencoded)
            boolean first = true;
            for (JKeyVal keyVal : data) {
               if (!first) {
                  stream.write("&");
               } else {
                  first = false;
               }
               stream.write(keyVal.key());
               stream.write("=");
               stream.write(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);
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy