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