add HttpInputStream

This commit is contained in:
Looly
2019-08-16 15:04:42 +08:00
parent 6b011af032
commit 9e39667e5a
10 changed files with 174 additions and 50 deletions

View File

@@ -16,6 +16,7 @@
* 【core】 强化ExceptionUtilissue#459@Github * 【core】 强化ExceptionUtilissue#459@Github
* 【core】 增强日期工具类pr#455@Github * 【core】 增强日期工具类pr#455@Github
* 【setting】 构造Setting增加默认字符编码 * 【setting】 构造Setting增加默认字符编码
* 【extra】 ServletUtil增加getHeaderMap方法
### Bug修复 ### Bug修复
* 【cache】 修复missCount规则issue#465@Github * 【cache】 修复missCount规则issue#465@Github

View File

@@ -55,10 +55,10 @@ public class WatchMonitor extends Thread implements Closeable, Serializable{
public static final WatchEvent.Kind<?> ENTRY_DELETE = StandardWatchEventKinds.ENTRY_DELETE; public static final WatchEvent.Kind<?> ENTRY_DELETE = StandardWatchEventKinds.ENTRY_DELETE;
/** 全部事件 */ /** 全部事件 */
public static final WatchEvent.Kind<?>[] EVENTS_ALL = {// public static final WatchEvent.Kind<?>[] EVENTS_ALL = {//
StandardWatchEventKinds.OVERFLOW, //事件丢失 OVERFLOW, //事件丢失
StandardWatchEventKinds.ENTRY_MODIFY, //修改 ENTRY_MODIFY, //修改
StandardWatchEventKinds.ENTRY_CREATE, //创建 ENTRY_CREATE, //创建
StandardWatchEventKinds.ENTRY_DELETE //删除 ENTRY_DELETE //删除
}; };
/** 监听路径,必须为目录 */ /** 监听路径,必须为目录 */

View File

@@ -5,9 +5,9 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer; import java.io.Writer;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.Enumeration; import java.util.Enumeration;
@@ -267,6 +267,27 @@ public class ServletUtil {
} }
// --------------------------------------------------------- Header start // --------------------------------------------------------- Header start
/**
* 获取请求所有的头header信息
*
* @param request 请求对象{@link HttpServletRequest}
* @return header值
* @since 4.6.2
*/
public static Map<String, String> getHeaderMap(HttpServletRequest request) {
final Map<String, String> headerMap = new HashMap<>();
final Enumeration<String> names = request.getHeaderNames();
String name = null;
while (names.hasMoreElements()) {
name = names.nextElement();
headerMap.put(name, request.getHeader(name));
}
return headerMap;
}
/** /**
* 忽略大小写获得请求header中的信息 * 忽略大小写获得请求header中的信息
* *
@@ -274,8 +295,8 @@ public class ServletUtil {
* @param nameIgnoreCase 忽略大小写头信息的KEY * @param nameIgnoreCase 忽略大小写头信息的KEY
* @return header值 * @return header值
*/ */
public final static String getHeaderIgnoreCase(HttpServletRequest request, String nameIgnoreCase) { public static String getHeaderIgnoreCase(HttpServletRequest request, String nameIgnoreCase) {
Enumeration<String> names = request.getHeaderNames(); final Enumeration<String> names = request.getHeaderNames();
String name = null; String name = null;
while (names.hasMoreElements()) { while (names.hasMoreElements()) {
name = names.nextElement(); name = names.nextElement();
@@ -286,6 +307,18 @@ public class ServletUtil {
return null; return null;
} }
/**
* 获得请求header中的信息
*
* @param request 请求对象{@link HttpServletRequest}
* @param name 头信息的KEY
* @param charsetName 字符集
* @return header值
*/
public static String getHeader(HttpServletRequest request, String name, String charsetName) {
return getHeader(request, name, CharsetUtil.charset(charsetName));
}
/** /**
* 获得请求header中的信息 * 获得请求header中的信息
@@ -294,15 +327,12 @@ public class ServletUtil {
* @param name 头信息的KEY * @param name 头信息的KEY
* @param charset 字符集 * @param charset 字符集
* @return header值 * @return header值
* @since 4.6.2
*/ */
public final static String getHeader(HttpServletRequest request, String name, String charset) { public static String getHeader(HttpServletRequest request, String name, Charset charset) {
final String header = request.getHeader(name); final String header = request.getHeader(name);
if (null != header) { if (null != header) {
try { return CharsetUtil.convert(header, CharsetUtil.CHARSET_ISO_8859_1, charset);
return new String(header.getBytes(CharsetUtil.ISO_8859_1), charset);
} catch (UnsupportedEncodingException e) {
throw new UtilException(StrUtil.format("Error charset {} for http request header.", charset));
}
} }
return null; return null;
} }
@@ -375,7 +405,7 @@ public class ServletUtil {
* @param name cookie名 * @param name cookie名
* @return Cookie对象 * @return Cookie对象
*/ */
public final static Cookie getCookie(HttpServletRequest httpServletRequest, String name) { public static Cookie getCookie(HttpServletRequest httpServletRequest, String name) {
final Map<String, Cookie> cookieMap = readCookieMap(httpServletRequest); final Map<String, Cookie> cookieMap = readCookieMap(httpServletRequest);
return cookieMap == null ? null : cookieMap.get(name); return cookieMap == null ? null : cookieMap.get(name);
} }
@@ -386,7 +416,7 @@ public class ServletUtil {
* @param httpServletRequest {@link HttpServletRequest} * @param httpServletRequest {@link HttpServletRequest}
* @return Cookie map * @return Cookie map
*/ */
public final static Map<String, Cookie> readCookieMap(HttpServletRequest httpServletRequest) { public static Map<String, Cookie> readCookieMap(HttpServletRequest httpServletRequest) {
Map<String, Cookie> cookieMap = new HashMap<String, Cookie>(); Map<String, Cookie> cookieMap = new HashMap<String, Cookie>();
Cookie[] cookies = httpServletRequest.getCookies(); Cookie[] cookies = httpServletRequest.getCookies();
if (null == cookies) { if (null == cookies) {
@@ -404,7 +434,7 @@ public class ServletUtil {
* @param response 响应对象{@link HttpServletResponse} * @param response 响应对象{@link HttpServletResponse}
* @param cookie Servlet Cookie对象 * @param cookie Servlet Cookie对象
*/ */
public final static void addCookie(HttpServletResponse response, Cookie cookie) { public static void addCookie(HttpServletResponse response, Cookie cookie) {
response.addCookie(cookie); response.addCookie(cookie);
} }
@@ -415,7 +445,7 @@ public class ServletUtil {
* @param name Cookie名 * @param name Cookie名
* @param value Cookie值 * @param value Cookie值
*/ */
public final static void addCookie(HttpServletResponse response, String name, String value) { public static void addCookie(HttpServletResponse response, String name, String value) {
response.addCookie(new Cookie(name, value)); response.addCookie(new Cookie(name, value));
} }
@@ -429,7 +459,7 @@ public class ServletUtil {
* @param path Cookie的有效路径 * @param path Cookie的有效路径
* @param domain the domain name within which this cookie is visible; form is according to RFC 2109 * @param domain the domain name within which this cookie is visible; form is according to RFC 2109
*/ */
public final static void addCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds, String path, String domain) { public static void addCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds, String path, String domain) {
Cookie cookie = new Cookie(name, value); Cookie cookie = new Cookie(name, value);
if (domain != null) { if (domain != null) {
cookie.setDomain(domain); cookie.setDomain(domain);
@@ -449,7 +479,7 @@ public class ServletUtil {
* @param value cookie值 * @param value cookie值
* @param maxAgeInSeconds -1: 关闭浏览器清除Cookie. 0: 立即清除Cookie. &gt;0 : Cookie存在的秒数. * @param maxAgeInSeconds -1: 关闭浏览器清除Cookie. 0: 立即清除Cookie. &gt;0 : Cookie存在的秒数.
*/ */
public final static void addCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds) { public static void addCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds) {
addCookie(response, name, value, maxAgeInSeconds, "/", null); addCookie(response, name, value, maxAgeInSeconds, "/", null);
} }

View File

@@ -20,7 +20,7 @@ import cn.hutool.extra.tokenizer.TokenizerException;
public class AnalysisEngine implements TokenizerEngine { public class AnalysisEngine implements TokenizerEngine {
private Analyzer analyzer; private Analyzer analyzer;
/** /**
* 构造 * 构造
* *

View File

@@ -4,8 +4,8 @@ import org.ansj.splitWord.Analysis;
import org.ansj.splitWord.analysis.ToAnalysis; import org.ansj.splitWord.analysis.ToAnalysis;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.tokenizer.TokenizerEngine;
import cn.hutool.extra.tokenizer.Result; import cn.hutool.extra.tokenizer.Result;
import cn.hutool.extra.tokenizer.TokenizerEngine;
/** /**
* Ansj分词引擎实现<br> * Ansj分词引擎实现<br>
@@ -38,5 +38,5 @@ public class AnsjEngine implements TokenizerEngine {
public Result parse(CharSequence text) { public Result parse(CharSequence text) {
return new AnsjResult(analysis.parseStr(StrUtil.str(text))); return new AnsjResult(analysis.parseStr(StrUtil.str(text)));
} }
} }

View File

@@ -39,5 +39,5 @@ public class HanLPEngine implements TokenizerEngine {
public Result parse(CharSequence text) { public Result parse(CharSequence text) {
return new HanLPResult(this.seg.seg(StrUtil.str(text))); return new HanLPResult(this.seg.seg(StrUtil.str(text)));
} }
} }

View File

@@ -39,5 +39,4 @@ public class IKAnalyzerEngine implements TokenizerEngine {
this.seg.reset(StrUtil.getReader(text)); this.seg.reset(StrUtil.getReader(text));
return new IKAnalyzerResult(this.seg); return new IKAnalyzerResult(this.seg);
} }
} }

View File

@@ -0,0 +1,111 @@
package cn.hutool.http;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.DeflaterInputStream;
import java.util.zip.GZIPInputStream;
import cn.hutool.core.util.StrUtil;
/**
* HTTP输入流此流用于包装Http请求响应内容的流用于解析各种压缩、分段的响应流内容
*
* @author Looly
*
*/
public class HttpInputStream extends InputStream {
/** 原始流 */
private volatile InputStream in;
/**
* 构造
*
* @param response 响应对象
*/
public HttpInputStream(HttpResponse response) {
init(response);
}
@Override
public int read() throws IOException {
return this.in.read();
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return this.in.read(b, off, len);
}
@Override
public long skip(long n) throws IOException {
return this.in.skip(n);
}
@Override
public int available() throws IOException {
return this.in.available();
}
@Override
public void close() throws IOException {
this.in.close();
}
@Override
public synchronized void mark(int readlimit) {
this.in.mark(readlimit);
}
@Override
public synchronized void reset() throws IOException {
this.in.reset();
}
@Override
public boolean markSupported() {
return this.in.markSupported();
}
/**
* 初始化流
*
* @param response 响应对象
*/
private void init(HttpResponse response) {
try {
this.in = (response.status < HttpStatus.HTTP_BAD_REQUEST) ? response.httpConnection.getInputStream() : response.httpConnection.getErrorStream();
} catch (IOException e) {
if (e instanceof FileNotFoundException) {
// 服务器无返回内容,忽略之
} else {
throw new HttpException(e);
}
}
// 在一些情况下返回的流为null此时提供状态码说明
if (null == this.in) {
this.in = new ByteArrayInputStream(StrUtil.format("Error request, response status: {}", response.status).getBytes());
return;
}
// TODO 分段响应内容解析
if(response.isChunked()) {
}
if (response.isGzip() && false == (response.in instanceof GZIPInputStream)) {
// Accept-Encoding: gzip
try {
this.in = new GZIPInputStream(this.in);
} catch (IOException e) {
// 在类似于Head等方法中无body返回此时GZIPInputStream构造会出现错误在此忽略此错误读取普通数据
// ignore
}
} else if (response.isDeflate() && false == (this.in instanceof DeflaterInputStream)) {
// Accept-Encoding: defalte
this.in = new DeflaterInputStream(this.in);
}
}
}

View File

@@ -12,8 +12,6 @@ import java.net.HttpCookie;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.zip.DeflaterInputStream;
import java.util.zip.GZIPInputStream;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FastByteArrayOutputStream; import cn.hutool.core.io.FastByteArrayOutputStream;
@@ -38,13 +36,13 @@ import cn.hutool.log.StaticLog;
public class HttpResponse extends HttpBase<HttpResponse> implements Closeable { public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
/** 持有连接对象 */ /** 持有连接对象 */
private HttpConnection httpConnection; protected HttpConnection httpConnection;
/** Http请求原始流 */ /** Http请求原始流 */
private InputStream in; protected InputStream in;
/** 是否异步异步下只持有流否则将在初始化时直接读取body内容 */ /** 是否异步异步下只持有流否则将在初始化时直接读取body内容 */
private volatile boolean isAsync; private volatile boolean isAsync;
/** 响应状态码 */ /** 响应状态码 */
private int status; protected int status;
/** 是否忽略读取Http响应体 */ /** 是否忽略读取Http响应体 */
private boolean ignoreBody; private boolean ignoreBody;
/** 从响应中获取的编码 */ /** 从响应中获取的编码 */
@@ -373,9 +371,9 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
* @throws HttpException IO异常 * @throws HttpException IO异常
*/ */
private HttpResponse init() throws HttpException { private HttpResponse init() throws HttpException {
// 获取响应状态码
try { try {
this.status = httpConnection.responseCode(); this.status = httpConnection.responseCode();
this.in = (this.status < HttpStatus.HTTP_BAD_REQUEST) ? this.httpConnection.getInputStream() : this.httpConnection.getErrorStream();
} catch (IOException e) { } catch (IOException e) {
if (e instanceof FileNotFoundException) { if (e instanceof FileNotFoundException) {
// 服务器无返回内容,忽略之 // 服务器无返回内容,忽略之
@@ -394,31 +392,15 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
// 存储服务端设置的Cookie信息 // 存储服务端设置的Cookie信息
GlobalCookieManager.store(httpConnection); GlobalCookieManager.store(httpConnection);
// 获取响应编码
final Charset charset = httpConnection.getCharset(); final Charset charset = httpConnection.getCharset();
this.charsetFromResponse = charset; this.charsetFromResponse = charset;
if (null != charset) { if (null != charset) {
this.charset = charset; this.charset = charset;
} }
if (null == this.in) { // 获取响应内容流
// 在一些情况下返回的流为null此时提供状态码说明 this.in = new HttpInputStream(this);
this.in = new ByteArrayInputStream(StrUtil.format("Error request, response status: {}", this.status).getBytes());
} else {
// TODO 分段响应内容解析
if (isGzip() && false == (in instanceof GZIPInputStream)) {
// Accept-Encoding: gzip
try {
in = new GZIPInputStream(in);
} catch (IOException e) {
// 在类似于Head等方法中无body返回此时GZIPInputStream构造会出现错误在此忽略此错误读取普通数据
// ignore
}
} else if (isDeflate() && false == (in instanceof DeflaterInputStream)) {
// Accept-Encoding: defalte
in = new DeflaterInputStream(in);
}
}
// 同步情况下强制同步 // 同步情况下强制同步
return this.isAsync ? this : forceSync(); return this.isAsync ? this : forceSync();

View File

@@ -63,7 +63,8 @@ public class HttpUtilTest {
@Test @Test
@Ignore @Ignore
public void getTest5() { public void getTest5() {
String res = HttpUtil.get("https://comment.bilibili.com/67573272.xml"); String res = HttpRequest.get("https://comment.bilibili.com/67573272.xml")
.execute().body();
Console.log(res); Console.log(res);
} }