Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>5.6.237</version>
<version>5.6.238</version>
<packaging>jar</packaging>
<name>cos-java-sdk</name>
<description>java sdk for qcloud cos</description>
Expand Down
6 changes: 1 addition & 5 deletions src/main/java/com/qcloud/cos/COSClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,7 @@ public COSClient(COSCredentialsProvider credProvider, ClientConfig clientConfig)
super();
this.credProvider = credProvider;
this.clientConfig = clientConfig;
if (clientConfig.getRequestTimeOutEnable()) {
this.cosHttpClient = new TimeOutCosHttpClient(clientConfig);
} else {
this.cosHttpClient = new DefaultCosHttpClient(clientConfig);
}
this.cosHttpClient = new DefaultCosHttpClient(clientConfig);
}

public void shutdown() {
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/com/qcloud/cos/ClientConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ public class ClientConfig {

private long preflightStatusUpdateInterval = 10 * 1000L;

private boolean isRedirectsEnabled = false;

// 不传入region 用于后续调用List Buckets(获取所有的bucket信息)
public ClientConfig() {
super();
Expand Down Expand Up @@ -373,7 +375,7 @@ public void setRequestTimeOutEnable(boolean requestTimeOutEnable) {
}

public boolean getRequestTimeOutEnable() {
return isRequestTimeOutEnable;
return isRequestTimeOutEnable && (requestTimeout > 0);
}

public void setShutdownTimeout(int shutdownTimeout) {
Expand Down Expand Up @@ -467,4 +469,12 @@ public void setCheckPreflightStatus(boolean checkPreflightStatus) {
public long getPreflightStatusUpdateInterval() {
return preflightStatusUpdateInterval;
}

public boolean isRedirectsEnabled() {
return isRedirectsEnabled;
}

public void setRedirectsEnabled(boolean redirectsEnabled) {
isRedirectsEnabled = redirectsEnabled;
}
}
70 changes: 70 additions & 0 deletions src/main/java/com/qcloud/cos/http/CosHttpClientTimer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.qcloud.cos.http;

import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.internal.CosClientAbortTask;
import com.qcloud.cos.internal.CosClientAbortTaskMonitor;
import com.qcloud.cos.internal.DefaultClientAbortTaskImpl;
import com.qcloud.cos.internal.CosClientAbortTaskImpl;
import com.qcloud.cos.internal.CosClientAbortTaskMonitorImpl;

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

public class CosHttpClientTimer {
private volatile ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;

private static ThreadFactory getThreadFactory(final String name) {
return new ThreadFactory() {
private int threadCount = 1;

public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
if (name != null) {
thread.setName(name + "-" + threadCount++);
}
thread.setPriority(Thread.MAX_PRIORITY);
return thread;
}
};
}

private synchronized void initializeExecutor() {
if (scheduledThreadPoolExecutor == null) {
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5, getThreadFactory("COSClientRequestTimeOutThread"));
try {
executor.getClass().getMethod("setRemoveOnCancelPolicy", boolean.class).invoke(executor, Boolean.TRUE);
} catch (NoSuchMethodException e) {
throw new CosClientException("The request timeout feature is only available for Java 1.7 and above.");
} catch (SecurityException e) {
throw new CosClientException("The request timeout feature needs additional permissions to function.", e);
} catch (Exception e) {
throw new CosClientException(e);
}

executor.setKeepAliveTime(5, TimeUnit.SECONDS);
executor.allowCoreThreadTimeOut(true);

scheduledThreadPoolExecutor = executor;
}
}

public CosClientAbortTaskMonitor startTimer(int requestTimeout) {
if (requestTimeout <= 0) {
return DefaultClientAbortTaskImpl.INSTANCE;
} else if (scheduledThreadPoolExecutor == null) {
initializeExecutor();
}

CosClientAbortTask task = new CosClientAbortTaskImpl(Thread.currentThread());
ScheduledFuture<?> timerTaskFuture = scheduledThreadPoolExecutor.schedule(task, requestTimeout, TimeUnit.MILLISECONDS);
return new CosClientAbortTaskMonitorImpl(task, timerTaskFuture);
}

public synchronized void shutdown() {
if (scheduledThreadPoolExecutor != null) {
scheduledThreadPoolExecutor.shutdown();
}
}
}
12 changes: 12 additions & 0 deletions src/main/java/com/qcloud/cos/http/CosHttpRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.event.ProgressListener;
import com.qcloud.cos.exception.ExceptionLogDetail;
import com.qcloud.cos.internal.CosClientAbortTaskMonitor;
import com.qcloud.cos.internal.CosServiceRequest;
import com.qcloud.cos.internal.DefaultClientAbortTaskImpl;

public class CosHttpRequest<T extends CosServiceRequest> {

Expand Down Expand Up @@ -62,6 +64,8 @@ public class CosHttpRequest<T extends CosServiceRequest> {

private List<ExceptionLogDetail> logDetails = new ArrayList<ExceptionLogDetail>();

private CosClientAbortTaskMonitor clientAbortTaskMonitor = DefaultClientAbortTaskImpl.INSTANCE;

public CosHttpRequest(T originRequest) {
this.originRequest = originRequest;
this.ciSpecialEndParameter = originRequest.getCiSpecialEndParameter();
Expand Down Expand Up @@ -182,6 +186,14 @@ public List<ExceptionLogDetail> getExceptionsLogDetails() {
return logDetails;
}

public CosClientAbortTaskMonitor getClientAbortTaskMonitor() {
return clientAbortTaskMonitor;
}

public void setClientAbortTaskMonitor(CosClientAbortTaskMonitor clientAbortTaskMonitor) {
this.clientAbortTaskMonitor = clientAbortTaskMonitor;
}

@Override
public String toString() {
StringBuilder strBuilder = new StringBuilder();
Expand Down
72 changes: 70 additions & 2 deletions src/main/java/com/qcloud/cos/http/DefaultCosHttpClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import com.qcloud.cos.region.Region;
import com.qcloud.cos.event.ProgressInputStream;
import com.qcloud.cos.event.ProgressListener;
import com.qcloud.cos.exception.AbortedException;
import com.qcloud.cos.exception.ClientExceptionConstants;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
Expand All @@ -70,6 +71,7 @@
import com.qcloud.cos.internal.SdkBufferedInputStream;
import com.qcloud.cos.internal.CIWorkflowServiceRequest;
import com.qcloud.cos.internal.CIServiceRequest;
import com.qcloud.cos.internal.CosClientAbortTaskMonitor;
import com.qcloud.cos.retry.BackoffStrategy;
import com.qcloud.cos.retry.RetryPolicy;
import com.qcloud.cos.utils.CodecUtils;
Expand Down Expand Up @@ -124,6 +126,7 @@ public class DefaultCosHttpClient implements CosHttpClient {

private CosErrorResponseHandler errorResponseHandler;
private HandlerAfterProcess handlerAfterProcess;
private final CosHttpClientTimer cosHttpClientTimer;
private static final Logger log = LoggerFactory.getLogger(DefaultCosHttpClient.class);

public DefaultCosHttpClient(ClientConfig clientConfig) {
Expand Down Expand Up @@ -179,6 +182,7 @@ public InetAddress[] resolve(String host) throws UnknownHostException {
this.maxErrorRetry = clientConfig.getMaxErrorRetry();
this.retryPolicy = ValidationUtils.assertNotNull(clientConfig.getRetryPolicy(), "retry policy");
this.backoffStrategy = ValidationUtils.assertNotNull(clientConfig.getBackoffStrategy(), "backoff strategy");
this.cosHttpClientTimer = new CosHttpClientTimer();
initHttpClient();
}

Expand All @@ -201,7 +205,9 @@ private void initHttpClient() {
.setConnectionRequestTimeout(
this.clientConfig.getConnectionRequestTimeout())
.setConnectTimeout(this.clientConfig.getConnectionTimeout())
.setSocketTimeout(this.clientConfig.getSocketTimeout()).build();
.setSocketTimeout(this.clientConfig.getSocketTimeout())
.setRedirectsEnabled(this.clientConfig.isRedirectsEnabled())
.build();
this.idleConnectionMonitor = new IdleConnectionMonitorThread(this.connectionManager);
this.idleConnectionMonitor.setIdleAliveMS(this.clientConfig.getIdleConnectionAlive());
this.idleConnectionMonitor.setDaemon(true);
Expand All @@ -224,6 +230,7 @@ public void shutdown() {
}
log.info(trace.toString());
}
cosHttpClientTimer.shutdown();
this.idleConnectionMonitor.shutdown();
}

Expand Down Expand Up @@ -596,7 +603,11 @@ public <X, Y extends CosServiceRequest> X exeute(CosHttpRequest<Y> request,
httpRequest = buildHttpRequest(request);
httpResponse = null;
startTime = System.currentTimeMillis();
httpResponse = executeRequest(context, httpRequest);
if (clientConfig.getRequestTimeOutEnable()) {
httpResponse = executeRequestWithTimeout(context, httpRequest, request);
} else {
httpResponse = executeRequest(context, httpRequest);
}
checkResponse(request, httpRequest, httpResponse);
break;
} catch (CosServiceException cse) {
Expand Down Expand Up @@ -737,6 +748,63 @@ private HttpResponse executeRequest(HttpContext context, HttpRequestBase httpReq
return httpResponse;
}

private <Y extends CosServiceRequest> HttpResponse executeRequestWithTimer(HttpContext context, HttpRequestBase httpRequest, CosHttpRequest<Y> originRequest) throws Exception {
CosClientAbortTaskMonitor abortTaskMonitor = cosHttpClientTimer.startTimer(clientConfig.getRequestTimeout());
abortTaskMonitor.setCurrentHttpRequest(httpRequest);
HttpResponse httpResponse = null;
try {
originRequest.setClientAbortTaskMonitor(abortTaskMonitor);
httpResponse = executeOneRequest(context, httpRequest);
} catch (IOException ie) {
if (originRequest.getClientAbortTaskMonitor().hasTimeoutExpired()) {
Thread.interrupted();
String errorMsg = String.format("catch IOException when executing http request[%s], and execution aborted task has been done, exp:", originRequest);
log.error(errorMsg, ie);
throw new InterruptedException();
}
throw ie;
} finally {
originRequest.getClientAbortTaskMonitor().cancelTask();
}

return httpResponse;
}

private <Y extends CosServiceRequest> HttpResponse executeRequestWithTimeout(HttpContext context, HttpRequestBase httpRequest, CosHttpRequest<Y> originRequest) throws Exception {
try {
return executeRequestWithTimer(context, httpRequest, originRequest);
} catch (InterruptedException ie) {
if (originRequest.getClientAbortTaskMonitor().hasTimeoutExpired()) {
Thread.interrupted();
String errorMsg = "InterruptedException: time out after waiting " + this.clientConfig.getRequestTimeout()/1000 + " seconds";
throw new CosClientException(errorMsg, ClientExceptionConstants.REQUEST_TIMEOUT, ie);
}
if (!httpRequest.isAborted()) {
httpRequest.abort();
}
throw ie;
} catch (AbortedException ae) {
if (originRequest.getClientAbortTaskMonitor().hasTimeoutExpired()) {
Thread.interrupted();
String errorMsg = "AbortedException: time out after waiting " + this.clientConfig.getRequestTimeout()/1000 + " seconds";
throw new CosClientException(errorMsg, ClientExceptionConstants.REQUEST_TIMEOUT, ae);
}
if (!httpRequest.isAborted()) {
httpRequest.abort();
}
throw ae;
} catch (IOException ie) {
if (!httpRequest.isAborted()) {
httpRequest.abort();
}
throw ExceptionUtils.createClientException(ie);
} finally {
if (originRequest.getClientAbortTaskMonitor().hasTimeoutExpired()) {
Thread.interrupted();
}
}
}

private <Y extends CosServiceRequest> void handleLog(CosHttpRequest<Y> request) {
for (ExceptionLogDetail logDetail : request.getExceptionsLogDetails()) {
log.error(logDetail.getErrMsg(), logDetail.getException());
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/qcloud/cos/internal/CosClientAbortTask.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.qcloud.cos.internal;

import org.apache.http.client.methods.HttpRequestBase;

public interface CosClientAbortTask extends Runnable {
void setCurrentHttpRequest(HttpRequestBase newRequest);

boolean hasClientExecutionAborted();

boolean isEnabled();

void cancel();
}
62 changes: 62 additions & 0 deletions src/main/java/com/qcloud/cos/internal/CosClientAbortTaskImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.qcloud.cos.internal;

import org.apache.http.client.methods.HttpRequestBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CosClientAbortTaskImpl implements CosClientAbortTask {
private volatile boolean hasTaskExecuted;
private HttpRequestBase currentHttpRequest;
private final Thread thread;
private volatile boolean isCancelled;

private static final Logger log = LoggerFactory.getLogger(CosClientAbortTaskImpl.class);

private final Object lock = new Object();

public CosClientAbortTaskImpl(Thread thread) {
this.thread = thread;
}

@Override
public void run() {
synchronized (this.lock) {
if (isCancelled) {
return;
}
hasTaskExecuted = true;
if (!thread.isInterrupted()) {
log.debug("request timeout and current thread will be interrupted");
thread.interrupt();
}
if (!currentHttpRequest.isAborted()) {
log.debug("request timeout and current http request will be aborted");
currentHttpRequest.abort();
}
}
}

@Override
public void setCurrentHttpRequest(HttpRequestBase newRequest) {
this.currentHttpRequest = newRequest;
}

@Override
public boolean hasClientExecutionAborted() {
synchronized (this.lock) {
return hasTaskExecuted;
}
}

@Override
public boolean isEnabled() {
return true;
}

@Override
public void cancel() {
synchronized (this.lock) {
isCancelled = true;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.qcloud.cos.internal;

import org.apache.http.client.methods.HttpRequestBase;

public interface CosClientAbortTaskMonitor {
void setCurrentHttpRequest(HttpRequestBase newRequest);

boolean hasTimeoutExpired();

boolean isEnabled();

void cancelTask();
}
Loading