add parseCST and fix cookie bug

This commit is contained in:
Looly 2019-10-05 18:18:57 +08:00
parent 476285b302
commit a4515e218b
20 changed files with 198 additions and 120 deletions

View File

@ -8,10 +8,14 @@
### 新特性 ### 新特性
* 【all】 修复注释中的错别字issue#I12XE6@Gitee * 【all】 修复注释中的错别字issue#I12XE6@Gitee
* 【core】 CsvWriter支持其它类型的参数issue#I12XE3@Gitee * 【core】 CsvWriter支持其它类型的参数issue#I12XE3@Gitee
* 【core】 ClassScaner支持自定义ClassLoader * 【core】 ClassScanner支持自定义ClassLoader
* 【core】 修改错别字pr#568@Github
* 【core】 增加DateUtil.parseCST方法issue#570@Github
### Bug修复 ### Bug修复
* 【all】 修复阶乘计算错误bugissue#I12XE4@Gitee * 【all】 修复阶乘计算错误bugissue#I12XE4@Gitee
* 【http】 修复disableCookie无效问题issue#572@Github
* 【http】 修复HttpResponse.getCookies导致的问题issue#572@Github
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------

View File

@ -140,6 +140,7 @@ public class DateTime extends Date {
*/ */
public DateTime(Calendar calendar) { public DateTime(Calendar calendar) {
this(calendar.getTime(), calendar.getTimeZone()); this(calendar.getTime(), calendar.getTimeZone());
this.setFirstDayOfWeek(Week.of(calendar.getFirstDayOfWeek()));
} }
/** /**

View File

@ -757,6 +757,24 @@ public class DateUtil {
throw new DateException("No format fit for date String [{}] !", utcString); throw new DateException("No format fit for date String [{}] !", utcString);
} }
/**
* 解析CST时间格式<br>
* <ol>
* <li>EEE MMM dd HH:mm:ss z yyyy例如Wed Aug 01 00:00:00 CST 2012</li>
* </ol>
*
* @param cstString UTC时间
* @return 日期对象
* @since 4.6.9
*/
public static DateTime parseCST(String cstString) {
if (cstString == null) {
return null;
}
return parse(cstString, DatePattern.JDK_DATETIME_FORMAT);
}
/** /**
* 将日期字符串转换为{@link DateTime}对象格式<br> * 将日期字符串转换为{@link DateTime}对象格式<br>
* <ol> * <ol>
@ -808,8 +826,11 @@ public class DateUtil {
// HH:mm:ss 或者 HH:mm 时间格式匹配单独解析 // HH:mm:ss 或者 HH:mm 时间格式匹配单独解析
return parseTimeToday(dateStr); return parseTimeToday(dateStr);
} else if (StrUtil.containsAnyIgnoreCase(dateStr, wtb)) { } else if (StrUtil.containsAnyIgnoreCase(dateStr, wtb)) {
// JDK的Date对象toString默认格式类似于Tue Jun 4 16:25:15 +0800 2019 Thu May 16 17:57:18 GMT+08:00 2019 // JDK的Date对象toString默认格式类似于
return parse(dateStr, DatePattern.JDK_DATETIME_FORMAT); // Tue Jun 4 16:25:15 +0800 2019
// Thu May 16 17:57:18 GMT+08:00 2019
// Wed Aug 01 00:00:00 CST 2012
return parseCST(dateStr);
} else if (StrUtil.contains(dateStr, 'T')) { } else if (StrUtil.contains(dateStr, 'T')) {
// UTC时间 // UTC时间
return parseUTC(dateStr); return parseUTC(dateStr);

View File

@ -23,9 +23,9 @@ import java.util.jar.JarFile;
* 类扫描器 * 类扫描器
* *
* @author looly * @author looly
* @since 4.1.5 * @since 4.6.9
*/ */
public class ClassScaner implements Serializable { public class ClassScanner implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
@ -124,13 +124,13 @@ public class ClassScaner implements Serializable {
* @return 类集合 * @return 类集合
*/ */
public static Set<Class<?>> scanPackage(String packageName, Filter<Class<?>> classFilter) { public static Set<Class<?>> scanPackage(String packageName, Filter<Class<?>> classFilter) {
return new ClassScaner(packageName, classFilter).scan(); return new ClassScanner(packageName, classFilter).scan();
} }
/** /**
* 构造默认UTF-8编码 * 构造默认UTF-8编码
*/ */
public ClassScaner() { public ClassScanner() {
this(null); this(null);
} }
@ -139,7 +139,7 @@ public class ClassScaner implements Serializable {
* *
* @param packageName 包名所有包传入""或者null * @param packageName 包名所有包传入""或者null
*/ */
public ClassScaner(String packageName) { public ClassScanner(String packageName) {
this(packageName, null); this(packageName, null);
} }
@ -149,7 +149,7 @@ public class ClassScaner implements Serializable {
* @param packageName 包名所有包传入""或者null * @param packageName 包名所有包传入""或者null
* @param classFilter 过滤器无需传入null * @param classFilter 过滤器无需传入null
*/ */
public ClassScaner(String packageName, Filter<Class<?>> classFilter) { public ClassScanner(String packageName, Filter<Class<?>> classFilter) {
this(packageName, classFilter, CharsetUtil.CHARSET_UTF_8); this(packageName, classFilter, CharsetUtil.CHARSET_UTF_8);
} }
@ -160,7 +160,7 @@ public class ClassScaner implements Serializable {
* @param classFilter 过滤器无需传入null * @param classFilter 过滤器无需传入null
* @param charset 编码 * @param charset 编码
*/ */
public ClassScaner(String packageName, Filter<Class<?>> classFilter, Charset charset) { public ClassScanner(String packageName, Filter<Class<?>> classFilter, Charset charset) {
packageName = StrUtil.nullToEmpty(packageName); packageName = StrUtil.nullToEmpty(packageName);
this.packageName = packageName; this.packageName = packageName;
this.packageNameWithDot = StrUtil.addSuffixIfNot(packageName, StrUtil.DOT); this.packageNameWithDot = StrUtil.addSuffixIfNot(packageName, StrUtil.DOT);

View File

@ -26,7 +26,7 @@ public class NamedThreadFactory implements ThreadFactory {
/** 线程组 */ /** 线程组 */
private final AtomicInteger threadNumber = new AtomicInteger(1); private final AtomicInteger threadNumber = new AtomicInteger(1);
/** 是否守护线程 */ /** 是否守护线程 */
private final boolean isDeamon; private final boolean isDaemon;
/** 无法捕获的异常统一处理 */ /** 无法捕获的异常统一处理 */
private final UncaughtExceptionHandler handler; private final UncaughtExceptionHandler handler;
@ -34,10 +34,10 @@ public class NamedThreadFactory implements ThreadFactory {
* 构造 * 构造
* *
* @param prefix 线程名前缀 * @param prefix 线程名前缀
* @param isDeamon 是否守护线程 * @param isDaemon 是否守护线程
*/ */
public NamedThreadFactory(String prefix, boolean isDeamon) { public NamedThreadFactory(String prefix, boolean isDaemon) {
this(prefix, null, isDeamon); this(prefix, null, isDaemon);
} }
/** /**
@ -45,10 +45,10 @@ public class NamedThreadFactory implements ThreadFactory {
* *
* @param prefix 线程名前缀 * @param prefix 线程名前缀
* @param threadGroup 线程组可以为null * @param threadGroup 线程组可以为null
* @param isDeamon 是否守护线程 * @param isDaemon 是否守护线程
*/ */
public NamedThreadFactory(String prefix, ThreadGroup threadGroup, boolean isDeamon) { public NamedThreadFactory(String prefix, ThreadGroup threadGroup, boolean isDaemon) {
this(prefix, threadGroup, isDeamon, null); this(prefix, threadGroup, isDaemon, null);
} }
/** /**
@ -56,16 +56,16 @@ public class NamedThreadFactory implements ThreadFactory {
* *
* @param prefix 线程名前缀 * @param prefix 线程名前缀
* @param threadGroup 线程组可以为null * @param threadGroup 线程组可以为null
* @param isDeamon 是否守护线程 * @param isDaemon 是否守护线程
* @param handler 未捕获异常处理 * @param handler 未捕获异常处理
*/ */
public NamedThreadFactory(String prefix, ThreadGroup threadGroup, boolean isDeamon, UncaughtExceptionHandler handler) { public NamedThreadFactory(String prefix, ThreadGroup threadGroup, boolean isDaemon, UncaughtExceptionHandler handler) {
this.prefix = StrUtil.isBlank(prefix) ? "Hutool" : prefix; this.prefix = StrUtil.isBlank(prefix) ? "Hutool" : prefix;
if (null == threadGroup) { if (null == threadGroup) {
threadGroup = ThreadUtil.currentThreadGroup(); threadGroup = ThreadUtil.currentThreadGroup();
} }
this.group = threadGroup; this.group = threadGroup;
this.isDeamon = isDeamon; this.isDaemon = isDaemon;
this.handler = handler; this.handler = handler;
} }
@ -75,11 +75,11 @@ public class NamedThreadFactory implements ThreadFactory {
//守护线程 //守护线程
if (false == t.isDaemon()) { if (false == t.isDaemon()) {
if (isDeamon) { if (isDaemon) {
// 原线程为非守护则设置为守护 // 原线程为非守护则设置为守护
t.setDaemon(true); t.setDaemon(true);
} }
} else if (false == isDeamon) { } else if (false == isDaemon) {
// 原线程为守护则还原为非守护 // 原线程为守护则还原为非守护
t.setDaemon(false); t.setDaemon(false);
} }

View File

@ -98,17 +98,17 @@ public class ThreadUtil {
* 执行异步方法 * 执行异步方法
* *
* @param runnable 需要执行的方法体 * @param runnable 需要执行的方法体
* @param isDeamon 是否守护线程守护线程会在主线程结束后自动结束 * @param isDaemon 是否守护线程守护线程会在主线程结束后自动结束
* @return 执行的方法体 * @return 执行的方法体
*/ */
public static Runnable excAsync(final Runnable runnable, boolean isDeamon) { public static Runnable excAsync(final Runnable runnable, boolean isDaemon) {
Thread thread = new Thread() { Thread thread = new Thread() {
@Override @Override
public void run() { public void run() {
runnable.run(); runnable.run();
} }
}; };
thread.setDaemon(isDeamon); thread.setDaemon(isDaemon);
thread.start(); thread.start();
return runnable; return runnable;
@ -192,13 +192,13 @@ public class ThreadUtil {
* *
* @param runnable {@link Runnable} * @param runnable {@link Runnable}
* @param name 线程名 * @param name 线程名
* @param isDeamon 是否守护线程 * @param isDaemon 是否守护线程
* @return {@link Thread} * @return {@link Thread}
* @since 4.1.2 * @since 4.1.2
*/ */
public static Thread newThread(Runnable runnable, String name, boolean isDeamon) { public static Thread newThread(Runnable runnable, String name, boolean isDaemon) {
final Thread t = new Thread(null, runnable, name); final Thread t = new Thread(null, runnable, name);
t.setDaemon(isDeamon); t.setDaemon(isDaemon);
return t; return t;
} }
@ -396,11 +396,11 @@ public class ThreadUtil {
* 创建线程工厂 * 创建线程工厂
* *
* @param prefix 线程名前缀 * @param prefix 线程名前缀
* @param isDeamon 是否守护线程 * @param isDaemon 是否守护线程
* @since 4.0.0 * @since 4.0.0
*/ */
public static ThreadFactory newNamedThreadFactory(String prefix, boolean isDeamon) { public static ThreadFactory newNamedThreadFactory(String prefix, boolean isDaemon) {
return new NamedThreadFactory(prefix, isDeamon); return new NamedThreadFactory(prefix, isDaemon);
} }
/** /**
@ -408,11 +408,11 @@ public class ThreadUtil {
* *
* @param prefix 线程名前缀 * @param prefix 线程名前缀
* @param threadGroup 线程组可以为null * @param threadGroup 线程组可以为null
* @param isDeamon 是否守护线程 * @param isDaemon 是否守护线程
* @since 4.0.0 * @since 4.0.0
*/ */
public static ThreadFactory newNamedThreadFactory(String prefix, ThreadGroup threadGroup, boolean isDeamon) { public static ThreadFactory newNamedThreadFactory(String prefix, ThreadGroup threadGroup, boolean isDaemon) {
return new NamedThreadFactory(prefix, threadGroup, isDeamon); return new NamedThreadFactory(prefix, threadGroup, isDaemon);
} }
/** /**
@ -420,12 +420,12 @@ public class ThreadUtil {
* *
* @param prefix 线程名前缀 * @param prefix 线程名前缀
* @param threadGroup 线程组可以为null * @param threadGroup 线程组可以为null
* @param isDeamon 是否守护线程 * @param isDaemon 是否守护线程
* @param handler 未捕获异常处理 * @param handler 未捕获异常处理
* @since 4.0.0 * @since 4.0.0
*/ */
public static ThreadFactory newNamedThreadFactory(String prefix, ThreadGroup threadGroup, boolean isDeamon, UncaughtExceptionHandler handler) { public static ThreadFactory newNamedThreadFactory(String prefix, ThreadGroup threadGroup, boolean isDaemon, UncaughtExceptionHandler handler) {
return new NamedThreadFactory(prefix, threadGroup, isDeamon, handler); return new NamedThreadFactory(prefix, threadGroup, isDaemon, handler);
} }
/** /**

View File

@ -21,7 +21,7 @@ import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.ClassScaner; import cn.hutool.core.lang.ClassScanner;
import cn.hutool.core.lang.Filter; import cn.hutool.core.lang.Filter;
import cn.hutool.core.lang.Singleton; import cn.hutool.core.lang.Singleton;
@ -176,10 +176,10 @@ public class ClassUtil {
* @param packageName 包路径 * @param packageName 包路径
* @param annotationClass 注解类 * @param annotationClass 注解类
* @return 类集合 * @return 类集合
* @see ClassScaner#scanPackageByAnnotation(String, Class) * @see ClassScanner#scanPackageByAnnotation(String, Class)
*/ */
public static Set<Class<?>> scanPackageByAnnotation(String packageName, final Class<? extends Annotation> annotationClass) { public static Set<Class<?>> scanPackageByAnnotation(String packageName, final Class<? extends Annotation> annotationClass) {
return ClassScaner.scanPackageByAnnotation(packageName, annotationClass); return ClassScanner.scanPackageByAnnotation(packageName, annotationClass);
} }
/** /**
@ -188,20 +188,20 @@ public class ClassUtil {
* @param packageName 包路径 * @param packageName 包路径
* @param superClass 父类或接口 * @param superClass 父类或接口
* @return 类集合 * @return 类集合
* @see ClassScaner#scanPackageBySuper(String, Class) * @see ClassScanner#scanPackageBySuper(String, Class)
*/ */
public static Set<Class<?>> scanPackageBySuper(String packageName, final Class<?> superClass) { public static Set<Class<?>> scanPackageBySuper(String packageName, final Class<?> superClass) {
return ClassScaner.scanPackageBySuper(packageName, superClass); return ClassScanner.scanPackageBySuper(packageName, superClass);
} }
/** /**
* 扫面该包路径下所有class文件 * 扫面该包路径下所有class文件
* *
* @return 类集合 * @return 类集合
* @see ClassScaner#scanPackage() * @see ClassScanner#scanPackage()
*/ */
public static Set<Class<?>> scanPackage() { public static Set<Class<?>> scanPackage() {
return ClassScaner.scanPackage(); return ClassScanner.scanPackage();
} }
/** /**
@ -209,10 +209,10 @@ public class ClassUtil {
* *
* @param packageName 包路径 com | com. | com.abs | com.abs. * @param packageName 包路径 com | com. | com.abs | com.abs.
* @return 类集合 * @return 类集合
* @see ClassScaner#scanPackage(String) * @see ClassScanner#scanPackage(String)
*/ */
public static Set<Class<?>> scanPackage(String packageName) { public static Set<Class<?>> scanPackage(String packageName) {
return ClassScaner.scanPackage(packageName); return ClassScanner.scanPackage(packageName);
} }
/** /**
@ -225,7 +225,7 @@ public class ClassUtil {
* @return 类集合 * @return 类集合
*/ */
public static Set<Class<?>> scanPackage(String packageName, Filter<Class<?>> classFilter) { public static Set<Class<?>> scanPackage(String packageName, Filter<Class<?>> classFilter) {
return ClassScaner.scanPackage(packageName, classFilter); return ClassScanner.scanPackage(packageName, classFilter);
} }
// ----------------------------------------------------------------------------------------- Method // ----------------------------------------------------------------------------------------- Method

View File

@ -585,10 +585,7 @@ public class StrUtil {
*/ */
public static boolean startWith(CharSequence str, CharSequence prefix, boolean isIgnoreCase) { public static boolean startWith(CharSequence str, CharSequence prefix, boolean isIgnoreCase) {
if (null == str || null == prefix) { if (null == str || null == prefix) {
if (null == str && null == prefix) { return null == str && null == prefix;
return true;
}
return false;
} }
if (isIgnoreCase) { if (isIgnoreCase) {
@ -664,10 +661,7 @@ public class StrUtil {
*/ */
public static boolean endWith(CharSequence str, CharSequence suffix, boolean isIgnoreCase) { public static boolean endWith(CharSequence str, CharSequence suffix, boolean isIgnoreCase) {
if (null == str || null == suffix) { if (null == str || null == suffix) {
if (null == str && null == suffix) { return null == str && null == suffix;
return true;
}
return false;
} }
if (isIgnoreCase) { if (isIgnoreCase) {
@ -3831,7 +3825,7 @@ public class StrUtil {
} }
int preIndex = fromIndex; int preIndex = fromIndex;
int index = fromIndex; int index;
while ((index = indexOf(str, searchStr, preIndex, ignoreCase)) > -1) { while ((index = indexOf(str, searchStr, preIndex, ignoreCase)) > -1) {
result.append(str.subSequence(preIndex, index)); result.append(str.subSequence(preIndex, index));
result.append(replacement); result.append(replacement);
@ -3985,7 +3979,7 @@ public class StrUtil {
* *
* @param str1 字符串1 * @param str1 字符串1
* @param str2 字符串2 * @param str2 字符串2
* @param scale * @param scale 相似度
* @return 相似度百分比 * @return 相似度百分比
* @since 3.2.3 * @since 3.2.3
*/ */

View File

@ -213,6 +213,20 @@ public class URLUtil {
return urls; return urls;
} }
/**
* 获取URL中域名部分
*
* @param url URL
* @return 域名的URI
* @since 4.6.9
*/
public static URI getHost(URL url){
if(null == url){
return null;
}
return toURI(url.getHost());
}
/** /**
* 补全相对路径 * 补全相对路径
* *

View File

@ -2,6 +2,7 @@ package cn.hutool.core.date;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.BetweenFormater.Level; import cn.hutool.core.date.BetweenFormater.Level;
import cn.hutool.core.lang.Console;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -472,6 +473,16 @@ public class DateUtilTest {
Assert.assertEquals("2018-09-13 13:34:39.999", dateStr); Assert.assertEquals("2018-09-13 13:34:39.999", dateStr);
} }
@Test
public void parseCSTTest(){
String dateStr = "Wed Sep 16 11:26:23 CST 2009";
DateTime dateTime = DateUtil.parseCST(dateStr);
Assert.assertEquals("2009-09-17 01:26:23", dateTime.toString());
dateTime = DateUtil.parse(dateStr);
Assert.assertEquals("2009-09-17 01:26:23", dateTime.toString());
}
@Test @Test
public void parseJDkTest() { public void parseJDkTest() {
String dateStr = "Thu May 16 17:57:18 GMT+08:00 2019"; String dateStr = "Thu May 16 17:57:18 GMT+08:00 2019";

View File

@ -10,7 +10,7 @@ public class ClassScanerTest {
@Test @Test
@Ignore @Ignore
public void scanTest() { public void scanTest() {
ClassScaner scaner = new ClassScaner("cn.hutool.core.util", null); ClassScanner scaner = new ClassScanner("cn.hutool.core.util", null);
Set<Class<?>> set = scaner.scan(); Set<Class<?>> set = scaner.scan();
for (Class<?> clazz : set) { for (Class<?> clazz : set) {
Console.log(clazz.getName()); Console.log(clazz.getName());

View File

@ -1,8 +1,13 @@
package cn.hutool.core.util; package cn.hutool.core.util;
import cn.hutool.core.lang.Console;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
/** /**
* URLUtil单元测试 * URLUtil单元测试
* *
@ -55,6 +60,14 @@ public class URLUtilTest {
Assert.assertEquals("http://www.hutool.cn/aaa/bbb?a=1&b=2", normalize); Assert.assertEquals("http://www.hutool.cn/aaa/bbb?a=1&b=2", normalize);
} }
@Test
public void getHostTest() throws MalformedURLException {
String url = "//www.hutool.cn//aaa/\\bbb?a=1&b=2";
String normalize = URLUtil.normalize(url);
URI host = URLUtil.getHost(new URL(normalize));
Assert.assertEquals("www.hutool.cn", host.toString());
}
@Test @Test
public void encodeTest() { public void encodeTest() {
String body = "366466 - 副本.jpg"; String body = "366466 - 副本.jpg";

View File

@ -145,9 +145,9 @@ public class CronUtil {
/** /**
* 开始 * 开始
* *
* @param isDeamon 是否以守护线程方式启动如果为true则在调用{@link #stop()}方法后执行的定时任务立即结束否则等待执行完毕才结束 * @param isDaemon 是否以守护线程方式启动如果为true则在调用{@link #stop()}方法后执行的定时任务立即结束否则等待执行完毕才结束
*/ */
synchronized public static void start(boolean isDeamon) { synchronized public static void start(boolean isDaemon) {
if (scheduler.isStarted()) { if (scheduler.isStarted()) {
throw new UtilException("Scheduler has been started, please stop it first!"); throw new UtilException("Scheduler has been started, please stop it first!");
} }
@ -167,7 +167,7 @@ public class CronUtil {
} }
schedule(crontabSetting); schedule(crontabSetting);
scheduler.start(isDeamon); scheduler.start(isDaemon);
} }
/** /**

View File

@ -126,7 +126,7 @@ public class Scheduler implements Serializable {
* *
* @return 是否为守护线程 * @return 是否为守护线程
*/ */
public boolean isDeamon() { public boolean isDaemon() {
return this.daemon; return this.daemon;
} }
@ -362,11 +362,11 @@ public class Scheduler implements Serializable {
/** /**
* 启动 * 启动
* *
* @param isDeamon 是否以守护线程方式启动如果为true则在调用{@link #stop()}方法后执行的定时任务立即结束否则等待执行完毕才结束 * @param isDaemon 是否以守护线程方式启动如果为true则在调用{@link #stop()}方法后执行的定时任务立即结束否则等待执行完毕才结束
* @return this * @return this
*/ */
public Scheduler start(boolean isDeamon) { public Scheduler start(boolean isDaemon) {
this.daemon = isDeamon; this.daemon = isDaemon;
return start(); return start();
} }

View File

@ -50,8 +50,12 @@ import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
public class KeyUtil { public class KeyUtil {
/** Java密钥库(Java Key StoreJKS)KEY_STORE */ /** Java密钥库(Java Key StoreJKS)KEY_STORE */
public static final String KEY_STORE = "JKS"; public static final String KEY_TYPE_JKS = "JKS";
public static final String X509 = "X.509"; /** jceks */
public static final String KEY_TYPE_JCEKS = "jceks";
/** PKCS12是公钥加密标准它规定了可包含所有私钥、公钥和证书。其以二进制格式存储也称为 PFX 文件 */
public static final String KEY_TYPE_PKCS12 = "pkcs12";
public static final String KEY_TYPE_X509 = "X.509";
/** /**
* 默认密钥字节数 * 默认密钥字节数
@ -113,7 +117,7 @@ public class KeyUtil {
*/ */
public static SecretKey generateKey(String algorithm, byte[] key) { public static SecretKey generateKey(String algorithm, byte[] key) {
Assert.notBlank(algorithm, "Algorithm is blank!"); Assert.notBlank(algorithm, "Algorithm is blank!");
SecretKey secretKey = null; SecretKey secretKey;
if (algorithm.startsWith("PBE")) { if (algorithm.startsWith("PBE")) {
// PBE密钥 // PBE密钥
secretKey = generatePBEKey(algorithm, (null == key) ? null : StrUtil.str(key, CharsetUtil.CHARSET_UTF_8).toCharArray()); secretKey = generatePBEKey(algorithm, (null == key) ? null : StrUtil.str(key, CharsetUtil.CHARSET_UTF_8).toCharArray());
@ -139,7 +143,7 @@ public class KeyUtil {
throw new CryptoException("Algorithm [{}] is not a DES algorithm!"); throw new CryptoException("Algorithm [{}] is not a DES algorithm!");
} }
SecretKey secretKey = null; SecretKey secretKey;
if (null == key) { if (null == key) {
secretKey = generateKey(algorithm); secretKey = generateKey(algorithm);
} else { } else {
@ -608,7 +612,7 @@ public class KeyUtil {
* @return {@link KeyStore} * @return {@link KeyStore}
*/ */
public static KeyStore readJKSKeyStore(InputStream in, char[] password) { public static KeyStore readJKSKeyStore(InputStream in, char[] password) {
return readKeyStore(KEY_STORE, in, password); return readKeyStore(KEY_TYPE_JKS, in, password);
} }
/** /**
@ -618,11 +622,11 @@ public class KeyUtil {
* *
* @param type 类型 * @param type 类型
* @param in {@link InputStream} 如果想从文件读取.keystore文件使用 {@link FileUtil#getInputStream(java.io.File)} 读取 * @param in {@link InputStream} 如果想从文件读取.keystore文件使用 {@link FileUtil#getInputStream(java.io.File)} 读取
* @param password 密码 * @param password 密码null表示无密码
* @return {@link KeyStore} * @return {@link KeyStore}
*/ */
public static KeyStore readKeyStore(String type, InputStream in, char[] password) { public static KeyStore readKeyStore(String type, InputStream in, char[] password) {
KeyStore keyStore = null; KeyStore keyStore;
try { try {
keyStore = KeyStore.getInstance(type); keyStore = KeyStore.getInstance(type);
keyStore.load(in, password); keyStore.load(in, password);
@ -680,7 +684,7 @@ public class KeyUtil {
* @since 4.4.1 * @since 4.4.1
*/ */
public static Certificate readX509Certificate(InputStream in, char[] password, String alias) { public static Certificate readX509Certificate(InputStream in, char[] password, String alias) {
return readCertificate(X509, in, password, alias); return readCertificate(KEY_TYPE_X509, in, password, alias);
} }
/** /**
@ -710,7 +714,7 @@ public class KeyUtil {
* @since 4.4.1 * @since 4.4.1
*/ */
public static Certificate readX509Certificate(InputStream in) { public static Certificate readX509Certificate(InputStream in) {
return readCertificate(X509, in); return readCertificate(KEY_TYPE_X509, in);
} }
/** /**

View File

@ -27,7 +27,7 @@ public abstract class HttpBase<T> {
public static final String HTTP_1_1 = "HTTP/1.1"; public static final String HTTP_1_1 = "HTTP/1.1";
/**存储头信息*/ /**存储头信息*/
protected Map<String, List<String>> headers = new HashMap<String, List<String>>(); protected Map<String, List<String>> headers = new HashMap<>();
/**编码*/ /**编码*/
protected Charset charset = CharsetUtil.CHARSET_UTF_8; protected Charset charset = CharsetUtil.CHARSET_UTF_8;
/**http版本*/ /**http版本*/
@ -90,7 +90,7 @@ public abstract class HttpBase<T> {
if(null != name && null != value){ if(null != name && null != value){
final List<String> values = headers.get(name.trim()); final List<String> values = headers.get(name.trim());
if(isOverride || CollectionUtil.isEmpty(values)) { if(isOverride || CollectionUtil.isEmpty(values)) {
final ArrayList<String> valueList = new ArrayList<String>(); final ArrayList<String> valueList = new ArrayList<>();
valueList.add(value); valueList.add(value);
headers.put(name.trim(), valueList); headers.put(name.trim(), valueList);
}else { }else {

View File

@ -1,44 +1,31 @@
package cn.hutool.http; package cn.hutool.http;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.CookieManager;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URLStreamHandler;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import cn.hutool.core.codec.Base64; import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.BytesResource; import cn.hutool.core.io.resource.*;
import cn.hutool.core.io.resource.FileResource;
import cn.hutool.core.io.resource.MultiFileResource;
import cn.hutool.core.io.resource.MultiResource;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.*;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.cookie.GlobalCookieManager; import cn.hutool.http.cookie.GlobalCookieManager;
import cn.hutool.http.ssl.SSLSocketFactoryBuilder; import cn.hutool.http.ssl.SSLSocketFactoryBuilder;
import cn.hutool.json.JSON; import cn.hutool.json.JSON;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.*;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/** /**
* http请求类<br> * http请求类<br>
* Http请求类用于构建Http请求并同步获取结果此类通过CookieManager持有域名对应的Cookie值再次请求时会自动附带Cookie信息 * Http请求类用于构建Http请求并同步获取结果此类通过CookieManager持有域名对应的Cookie值再次请求时会自动附带Cookie信息
@ -987,8 +974,6 @@ public class HttpRequest extends HttpBase<HttpRequest> {
.setHttpsInfo(this.hostnameVerifier, this.ssf)// .setHttpsInfo(this.hostnameVerifier, this.ssf)//
.setConnectTimeout(this.connectionTimeout)// .setConnectTimeout(this.connectionTimeout)//
.setReadTimeout(this.readTimeout)// .setReadTimeout(this.readTimeout)//
// 自定义Cookie
.setCookie(this.cookie)
// 定义转发 // 定义转发
.setInstanceFollowRedirects(this.maxRedirectCount > 0) .setInstanceFollowRedirects(this.maxRedirectCount > 0)
// 流方式上传数据 // 流方式上传数据
@ -996,8 +981,13 @@ public class HttpRequest extends HttpBase<HttpRequest> {
// 覆盖默认Header // 覆盖默认Header
.header(this.headers, true); .header(this.headers, true);
if (null != this.cookie) {
// 当用户自定义Cookie时全局Cookie自动失效
this.httpConnection.setCookie(this.cookie);
} else {
// 读取全局Cookie信息并附带到请求中 // 读取全局Cookie信息并附带到请求中
GlobalCookieManager.add(this.httpConnection); GlobalCookieManager.add(this.httpConnection);
}
// 是否禁用缓存 // 是否禁用缓存
if (this.isDisableCache) { if (this.isDisableCache) {
@ -1193,8 +1183,6 @@ public class HttpRequest extends HttpBase<HttpRequest> {
/** /**
* 设置表单类型为Multipart文件上传 * 设置表单类型为Multipart文件上传
*
* @return HttpConnection HTTP连接对象
*/ */
private void setMultipart() { private void setMultipart() {
this.httpConnection.header(Header.CONTENT_TYPE, CONTENT_TYPE_MULTIPART_PREFIX + BOUNDARY, true); this.httpConnection.header(Header.CONTENT_TYPE, CONTENT_TYPE_MULTIPART_PREFIX + BOUNDARY, true);

View File

@ -151,10 +151,9 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
* *
* @return Cookie列表 * @return Cookie列表
* @since 3.1.1 * @since 3.1.1
* @see GlobalCookieManager#getCookieManager()
*/ */
public List<HttpCookie> getCookies() { public List<HttpCookie> getCookies() {
return GlobalCookieManager.getCookieManager().getCookieStore().getCookies(); return GlobalCookieManager.getCookies(this.httpConnection);
} }
/** /**
@ -374,11 +373,10 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
try { try {
this.status = httpConnection.responseCode(); this.status = httpConnection.responseCode();
} catch (IOException e) { } catch (IOException e) {
if (e instanceof FileNotFoundException) { if (false == (e instanceof FileNotFoundException)) {
// 服务器无返回内容忽略之
} else {
throw new HttpException(e); throw new HttpException(e);
} }
// 服务器无返回内容忽略之
} }
// 读取响应头信息 // 读取响应头信息
@ -410,7 +408,6 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
* 读取主体忽略EOFException异常 * 读取主体忽略EOFException异常
* *
* @param in 输入流 * @param in 输入流
* @return 自身
* @throws IORuntimeException IO异常 * @throws IORuntimeException IO异常
*/ */
private void readBody(InputStream in) throws IORuntimeException { private void readBody(InputStream in) throws IORuntimeException {

View File

@ -7,6 +7,8 @@ import cn.hutool.http.HttpConnection;
import java.io.IOException; import java.io.IOException;
import java.net.CookieManager; import java.net.CookieManager;
import java.net.CookiePolicy; import java.net.CookiePolicy;
import java.net.HttpCookie;
import java.net.URI;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -43,6 +45,17 @@ public class GlobalCookieManager {
return cookieManager; return cookieManager;
} }
/**
* 获取指定域名下所有Cookie信息
*
* @param conn HTTP连接
* @return Cookie信息列表
* @since 4.6.9
*/
public static List<HttpCookie> getCookies(HttpConnection conn){
return cookieManager.getCookieStore().get(getDomain(conn));
}
/** /**
* 将本地存储的Cookie信息附带到Http请求中不覆盖用户定义好的Cookie * 将本地存储的Cookie信息附带到Http请求中不覆盖用户定义好的Cookie
* *
@ -56,7 +69,7 @@ public class GlobalCookieManager {
Map<String, List<String>> cookieHeader; Map<String, List<String>> cookieHeader;
try { try {
cookieHeader = cookieManager.get(URLUtil.toURI(conn.getUrl()), new HashMap<String, List<String>>(0)); cookieHeader = cookieManager.get(getDomain(conn), new HashMap<String, List<String>>(0));
} catch (IOException e) { } catch (IOException e) {
throw new IORuntimeException(e); throw new IORuntimeException(e);
} }
@ -77,9 +90,18 @@ public class GlobalCookieManager {
} }
try { try {
cookieManager.put(URLUtil.toURI(conn.getUrl()), conn.headers()); cookieManager.put(getDomain(conn), conn.headers());
} catch (IOException e) { } catch (IOException e) {
throw new IORuntimeException(e); throw new IORuntimeException(e);
} }
} }
/**
* 获取连接的URL中域名信息例如http://www.hutool.cn/aaa/bb.html得到www.hutool.cn
* @param conn HttpConnection
* @return URI
*/
private static URI getDomain(HttpConnection conn){
return URLUtil.getHost(conn.getUrl());
}
} }

View File

@ -30,6 +30,15 @@ public class HttpRequestTest {
Console.log(body); Console.log(body);
} }
@Test
@Ignore
public void getCookiesTest() {
// 检查在Connection关闭情况下Cookie是否可以正常获取
HttpResponse res = HttpRequest.get("https://www.oschina.net/").execute();
String body = res.body();
Console.log(res.getCookies());
}
@Test @Test
@Ignore @Ignore
public void getWithParamsTest() { public void getWithParamsTest() {